├── README.md ├── config.default.js ├── .gitignore ├── package.json ├── LICENSE ├── util.js ├── public └── index.html ├── generate.js ├── test.ics ├── index.js └── fetch.js /README.md: -------------------------------------------------------------------------------- 1 | # UI-Calendar 2 | To fetch student's calendar and generate ics file 3 | -------------------------------------------------------------------------------- /config.default.js: -------------------------------------------------------------------------------- 1 | /* Course Planner 2 | * config file 3 | */ 4 | 5 | module.exports = { 6 | debug: false, 7 | mysql: { 8 | host: '', 9 | port: 0, 10 | user: '', 11 | password: '', 12 | database: '', 13 | acquireTimeout:10000 14 | } 15 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | 6 | # Runtime data 7 | pids 8 | *.pid 9 | *.seed 10 | 11 | # Directory for instrumented libs generated by jscoverage/JSCover 12 | lib-cov 13 | 14 | # Coverage directory used by tools like istanbul 15 | coverage 16 | 17 | # nyc test coverage 18 | .nyc_output 19 | 20 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 21 | .grunt 22 | 23 | # node-waf configuration 24 | .lock-wscript 25 | 26 | # Compiled binary addons (http://nodejs.org/api/addons.html) 27 | build/Release 28 | 29 | # Dependency directories 30 | node_modules 31 | jspm_packages 32 | 33 | # Optional npm cache directory 34 | .npm 35 | 36 | # Optional REPL history 37 | .node_repl_history 38 | 39 | # config 40 | config.js 41 | *.temp 42 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ui-calendar", 3 | "version": "1.0.0", 4 | "description": "To fetch student calendar and generate ics file", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "git+https://rijn@github.com/rijn/UI-Calendar.git" 12 | }, 13 | "author": "Rijn", 14 | "license": "MIT", 15 | "bugs": { 16 | "url": "https://github.com/rijn/UI-Calendar/issues" 17 | }, 18 | "homepage": "https://github.com/rijn/UI-Calendar#readme", 19 | "dependencies": { 20 | "colors": "^1.1.2", 21 | "express": "^4.14.0", 22 | "htmlparser2": "^3.9.1", 23 | "https": "^1.0.0", 24 | "ical-generator": "^0.2.7", 25 | "md5": "^2.1.0", 26 | "mysql": "github:mysqljs/mysql", 27 | "q": "^1.4.1", 28 | "querystring": "^0.2.0" 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016 Rijn Bian 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /util.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | 3 | combineCookies: function(newCookie, sharedObject) { 4 | if (newCookie) { 5 | for (var i = 0; i < newCookie.length; i++) { 6 | newCookie[i] = newCookie[i].split(';')[0]; 7 | } 8 | } 9 | for (var i in newCookie) { 10 | var key = newCookie[i].split('=')[0]; 11 | for (var j = 0; j < sharedObject.cookie.length; j++) { 12 | if (sharedObject.cookie[j].indexOf(key) >= 0) { 13 | sharedObject.cookie[j] = newCookie[i]; 14 | key = 'COOKIEFLAG'; 15 | } 16 | } 17 | if (key != 'COOKIEFLAG') { 18 | sharedObject.cookie.push(newCookie[i]); 19 | } 20 | } 21 | }, 22 | 23 | getConfig: function() { 24 | var defaultConfig = require('./config.default'); 25 | var config = defaultConfig; 26 | 27 | var arguments = process.argv.splice(2); 28 | 29 | try { 30 | var userConfig = require('./config'); 31 | } catch (e) { 32 | console.log('Could not find user config'); 33 | return; 34 | } 35 | 36 | for (var i in defaultConfig) { 37 | if (userConfig.hasOwnProperty(i)) { 38 | config[i] = userConfig[i]; 39 | } 40 | } 41 | 42 | return config; 43 | }, 44 | 45 | throwErr: function(err) { 46 | const colors = require('colors'); 47 | console.log('\n', err.red); 48 | throw err; 49 | }, 50 | 51 | colorSetTheme: function(colors) { 52 | colors.setTheme({ 53 | info: 'green', 54 | data: 'grey', 55 | help: 'cyan', 56 | warn: 'yellow', 57 | debug: 'blue', 58 | error: 'red', 59 | request: 'cyan', 60 | result: 'green', 61 | }); 62 | }, 63 | }; 64 | -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 |

UI-Calendar

8 |

To fetch student calendar and generate ics link

9 |

Open Source on GitHub

10 |
11 | 12 | 13 |
14 |
15 | 16 | 17 |
18 |
19 | 20 |
21 |

ready

22 | 60 | 61 | -------------------------------------------------------------------------------- /generate.js: -------------------------------------------------------------------------------- 1 | const Q = require('q'); 2 | 3 | Date.prototype.Format = function(fmt) { //author: meizz 4 | var o = { 5 | "M+": this.getMonth() + 1, //月份 6 | "d+": this.getDate(), //日 7 | "h+": this.getHours(), //小时 8 | "m+": this.getMinutes(), //分 9 | "s+": this.getSeconds(), //秒 10 | "q+": Math.floor((this.getMonth() + 3) / 3), //季度 11 | "S": this.getMilliseconds() //毫秒 12 | }; 13 | if (/(y+)/.test(fmt)) fmt = fmt.replace(RegExp.$1, (this.getFullYear() + "").substr(4 - RegExp.$1.length)); 14 | for (var k in o) 15 | if (new RegExp("(" + k + ")").test(fmt)) fmt = fmt.replace(RegExp.$1, (RegExp.$1.length == 1) ? (o[k]) : (("00" + o[k]).substr(("" + o[k]).length))); 16 | return fmt; 17 | } 18 | 19 | module.exports = function(sharedObject) { 20 | 21 | var deferred = Q.defer(); 22 | 23 | var ical = require('ical-generator'), 24 | cal = ical({ 25 | domain: 'rijnx.com', 26 | name: 'UI', 27 | prodId: { company: 'rijnx.com', product: 'UI Clandar' }, 28 | // timezone: 'America/Chicago' 29 | }).ttl(60 * 60 * 24);; 30 | 31 | // overwrite domain 32 | cal.domain('rijnx.com'); 33 | 34 | 35 | var arr = sharedObject.course; 36 | 37 | var weekRemap = { 38 | 'M': 'MO', 39 | 'T': 'TU', 40 | 'W': 'WE', 41 | 'R': 'TH', 42 | 'F': 'FR', 43 | } 44 | 45 | var now = new Date('2016-08-21'); 46 | var nowTime = now.getTime(); 47 | var oneDayLong = 24 * 60 * 60 * 1000; 48 | 49 | var firstWeekday = { 50 | 'MO': new Date(nowTime + 1 * oneDayLong).Format('yyyy-MM-dd'), 51 | 'TU': new Date(nowTime + 2 * oneDayLong).Format('yyyy-MM-dd'), 52 | 'WE': new Date(nowTime + 3 * oneDayLong).Format('yyyy-MM-dd'), 53 | 'TH': new Date(nowTime + 4 * oneDayLong).Format('yyyy-MM-dd'), 54 | 'FR': new Date(nowTime + 5 * oneDayLong).Format('yyyy-MM-dd') 55 | } 56 | 57 | for (var i = 0; i < arr.length; i++) { 58 | var temp = arr[i]; 59 | for (var j = 0; j < temp[5].length; j++) { 60 | temp[5][j] = weekRemap[temp[5][j]]; 61 | } 62 | cal.createEvent({ 63 | start: new Date(firstWeekday[temp[5][0]] + ' ' + temp[2]), 64 | end: new Date(firstWeekday[temp[5][0]] + ' ' + temp[3]), 65 | timezone: 'America/Chicago', 66 | summary: temp[0] + ' - ' + temp[1], 67 | description: temp[4], 68 | location: temp[4], 69 | // floating: true, 70 | repeating: { 71 | freq: 'WEEKLY', // required 72 | until: new Date('2016-12-08'), 73 | byDay: temp[5], 74 | } 75 | }); 76 | } 77 | 78 | sharedObject.ics = cal.toString(); 79 | // console.log(cal.toString()); 80 | 81 | // console.log(sharedObject.course); 82 | deferred.resolve(sharedObject); 83 | return deferred.promise; 84 | } 85 | -------------------------------------------------------------------------------- /test.ics: -------------------------------------------------------------------------------- 1 | BEGIN:VCALENDAR 2 | VERSION:2.0 3 | PRODID:-//rijnx.com//UI Clandar//EN 4 | NAME:UI 5 | X-WR-CALNAME:UI 6 | TIMEZONE-ID:America/Chicago 7 | X-WR-TIMEZONE:America/Chicago 8 | REFRESH-INTERVAL;VALUE=DURATION:P1D 9 | X-PUBLISHED-TTL:P1D 10 | BEGIN:VEVENT 11 | UID:5z0q@rijnx.com 12 | SEQUENCE:0 13 | DTSTAMP:20160814T220821Z 14 | DTSTART:20160822T180000Z 15 | DTEND:20160822T185000Z 16 | RRULE:FREQ=WEEKLY;UNTIL=20161208T000000Z;BYDAY=MO,WE 17 | SUMMARY:EALC 250 - Intro to Japanese Culture (Lecture) 18 | LOCATION:116 Roger Adams Laboratory 19 | DESCRIPTION:116 Roger Adams Laboratory 20 | END:VEVENT 21 | BEGIN:VEVENT 22 | UID:9xh1@rijnx.com 23 | SEQUENCE:0 24 | DTSTAMP:20160814T220821Z 25 | DTSTART:20160822T140000Z 26 | DTEND:20160822T145000Z 27 | RRULE:FREQ=WEEKLY;UNTIL=20161208T000000Z;BYDAY=MO,WE,FR 28 | SUMMARY:MATH 461 - Probability Theory (Lecture-Discussion) 29 | LOCATION:245 Altgeld Hall 30 | DESCRIPTION:245 Altgeld Hall 31 | END:VEVENT 32 | BEGIN:VEVENT 33 | UID:mzu4@rijnx.com 34 | SEQUENCE:0 35 | DTSTAMP:20160814T220821Z 36 | DTSTART:20160822T170000Z 37 | DTEND:20160822T175000Z 38 | RRULE:FREQ=WEEKLY;UNTIL=20161208T000000Z;BYDAY=MO,WE,FR 39 | SUMMARY:CS 241 - System Programming (Lecture) 40 | LOCATION:1320 Digital Computer Laboratory 41 | DESCRIPTION:1320 Digital Computer Laboratory 42 | END:VEVENT 43 | BEGIN:VEVENT 44 | UID:jj2z@rijnx.com 45 | SEQUENCE:0 46 | DTSTAMP:20160814T220821Z 47 | DTSTART:20160826T150000Z 48 | DTEND:20160826T155000Z 49 | RRULE:FREQ=WEEKLY;UNTIL=20161208T000000Z;BYDAY=FR 50 | SUMMARY:EALC 250 - Intro to Japanese Culture (Discussion/Recitation) 51 | LOCATION:G18 Foreign Languages Building 52 | DESCRIPTION:G18 Foreign Languages Building 53 | END:VEVENT 54 | BEGIN:VEVENT 55 | UID:74nl@rijnx.com 56 | SEQUENCE:0 57 | DTSTAMP:20160814T220821Z 58 | DTSTART:20160822T210000Z 59 | DTEND:20160822T215000Z 60 | RRULE:FREQ=WEEKLY;UNTIL=20161208T000000Z;BYDAY=MO,WE,FR 61 | SUMMARY:ESL 115 - Principles of Academic Writing (Lecture-Discussion) 62 | LOCATION:G52 Foreign Languages Building 63 | DESCRIPTION:G52 Foreign Languages Building 64 | END:VEVENT 65 | BEGIN:VEVENT 66 | UID:u131@rijnx.com 67 | SEQUENCE:0 68 | DTSTAMP:20160814T220821Z 69 | DTSTART:20160822T160000Z 70 | DTEND:20160822T165000Z 71 | RRULE:FREQ=WEEKLY;UNTIL=20161208T000000Z;BYDAY=MO,WE,FR 72 | SUMMARY:CS 233 - Computer Architecture (Lecture) 73 | LOCATION:1320 Digital Computer Laboratory 74 | DESCRIPTION:1320 Digital Computer Laboratory 75 | END:VEVENT 76 | BEGIN:VEVENT 77 | UID:i9i1@rijnx.com 78 | SEQUENCE:0 79 | DTSTAMP:20160814T220821Z 80 | DTSTART:20160823T210000Z 81 | DTEND:20160823T215000Z 82 | RRULE:FREQ=WEEKLY;UNTIL=20161208T000000Z;BYDAY=TU 83 | SUMMARY:CS 233 - Computer Architecture (Discussion/Recitation) 84 | LOCATION:1111 Siebel Center for Comp Sci 85 | DESCRIPTION:1111 Siebel Center for Comp Sci 86 | END:VEVENT 87 | BEGIN:VEVENT 88 | UID:t8f7@rijnx.com 89 | SEQUENCE:0 90 | DTSTAMP:20160814T220821Z 91 | DTSTART:20160823T220000Z 92 | DTEND:20160823T225000Z 93 | RRULE:FREQ=WEEKLY;UNTIL=20161208T000000Z;BYDAY=TU 94 | SUMMARY:CS 233 - Computer Architecture (Laboratory) 95 | LOCATION:0218 Siebel Center for Comp Sci 96 | DESCRIPTION:0218 Siebel Center for Comp Sci 97 | END:VEVENT 98 | BEGIN:VEVENT 99 | UID:rxxz@rijnx.com 100 | SEQUENCE:0 101 | DTSTAMP:20160814T220821Z 102 | DTSTART:20160825T170000Z 103 | DTEND:20160825T182000Z 104 | RRULE:FREQ=WEEKLY;UNTIL=20161208T000000Z;BYDAY=TH 105 | SUMMARY:CS 241 - System Programming (Discussion/Recitation) 106 | LOCATION:0222 Siebel Center for Comp Sci 107 | DESCRIPTION:0222 Siebel Center for Comp Sci 108 | END:VEVENT 109 | END:VCALENDAR -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | var express = require('express'); 2 | var app = express(); 3 | const Q = require('q'); 4 | const util = require('./util'); 5 | var fetch = require('./fetch'); 6 | var md5 = require('md5'); 7 | 8 | var config = util.getConfig(); 9 | 10 | var mysql = require('mysql'); 11 | var connection = mysql.createPool(config.mysql); 12 | 13 | var connectPool = function(sharedObject) { 14 | var deferred = Q.defer(); 15 | connection.getConnection(function(err, connection) { 16 | if (err) deferred.reject(err); 17 | deferred.resolve(sharedObject); 18 | }); 19 | return deferred.promise; 20 | } 21 | 22 | var getConst = function(sharedObject) { 23 | var deferred = Q.defer(); 24 | 25 | connection.query('SELECT `name`, `value` FROM `const`', function(err, rows, fields) { 26 | if (err) deferred.reject(err); 27 | 28 | sharedObject.ENV = {}; 29 | for (var i = 0; i < rows.length; i++) { 30 | sharedObject.ENV[rows[i].name] = rows[i].value; 31 | } 32 | 33 | deferred.resolve(sharedObject); 34 | }); 35 | 36 | return deferred.promise; 37 | } 38 | 39 | app.use(express.static('public')); 40 | 41 | app.get('/', function(req, res) { 42 | res.send('/index.html'); 43 | }); 44 | 45 | app.get('/save', function(req, res) { 46 | // res.send(req.query); 47 | fetch(req.query.netid, req.query.password, res) 48 | .then(function(sharedObject) { 49 | if (sharedObject.course[0] == null) { 50 | throw new Error; 51 | } 52 | return sharedObject; 53 | }) 54 | .then(connectPool) 55 | .then(getConst) 56 | .then(function(sharedObject) { 57 | // console.log(sharedObject.ENV); 58 | var deferred = Q.defer(); 59 | 60 | connection.query('SELECT COUNT(*) as count FROM `data` WHERE `netid` = ? and `md5` = ?', [req.query.netid, md5(req.query.netid + sharedObject.ENV.term)], function(err, rows, fields) { 61 | if (err) deferred.reject(err); 62 | 63 | sharedObject.count = rows[0].count; 64 | console.log(sharedObject.count); 65 | 66 | deferred.resolve(sharedObject); 67 | // console.log('The solution is: ', rows[0].solution); 68 | // res.send('OPERATE SUCCESSFULLY'); 69 | }); 70 | return deferred.promise; 71 | }) 72 | .then(function(sharedObject) { 73 | var deferred = Q.defer(); 74 | if (sharedObject.count > 0) { 75 | connection.query('UPDATE `data` SET `data` = ? WHERE `netid` = ? and `md5` = ?', [JSON.stringify(sharedObject.course), req.query.netid, md5(req.query.netid + sharedObject.ENV.term)], 76 | function(err, rows, fields) { 77 | if (err) deferred.reject(err); 78 | deferred.resolve(sharedObject); 79 | }); 80 | } else { 81 | connection.query('INSERT INTO `data` SET ?', { 82 | 'data': JSON.stringify(sharedObject.course), 83 | 'netid': req.query.netid, 84 | 'md5': md5(req.query.netid + sharedObject.ENV.term) 85 | }, 86 | function(err, rows, fields) { 87 | if (err) deferred.reject(err); 88 | deferred.resolve(sharedObject); 89 | }); 90 | } 91 | return deferred.promise; 92 | }) 93 | .then(function(sharedObject) { 94 | // connection.end(); 95 | res.send(md5(req.query.netid + sharedObject.ENV.term)); 96 | }) 97 | .fail(function(error) { 98 | // console.log(error); 99 | // connection.end(); 100 | res.send('ERROR'); 101 | }); 102 | }); 103 | 104 | var generate = require('./generate.js'); 105 | 106 | app.get('/get/:md5', function(req, res) { 107 | // res.send(req.params.md5); 108 | connectPool({}) 109 | .then(getConst) 110 | .then(function(sharedObject) { 111 | // console.log(sharedObject.ENV); 112 | var deferred = Q.defer(); 113 | 114 | connection.query('SELECT `data` FROM `data` WHERE `md5` = ?', [req.params.md5], function(err, rows, fields) { 115 | if (err) deferred.reject(err); 116 | 117 | sharedObject.data = rows[0].data; 118 | sharedObject.course = JSON.parse(sharedObject.data); 119 | // console.log(sharedObject.data); 120 | 121 | deferred.resolve(sharedObject); 122 | }); 123 | return deferred.promise; 124 | }) 125 | .then(generate) 126 | .then(function(sharedObject) { 127 | // connection.end(); 128 | res.send(sharedObject.ics); 129 | }) 130 | .fail(function(error) { 131 | // connection.end(); 132 | res.send('ERROR'); 133 | }); 134 | }); 135 | 136 | app.listen(3006, function() { 137 | console.log('Example app listening on port 3000!'); 138 | }); 139 | -------------------------------------------------------------------------------- /fetch.js: -------------------------------------------------------------------------------- 1 | // get arguments 2 | 3 | var arguments = process.argv.splice(2); 4 | console.log(arguments); 5 | 6 | const Q = require('q'); 7 | 8 | const https = require('https'); 9 | const querystring = require('querystring'); 10 | 11 | const util = require('./util'); 12 | 13 | const colors = require('colors'); 14 | util.colorSetTheme(colors); 15 | 16 | var config = util.getConfig(); 17 | 18 | var getCourses = function(netID, password) { 19 | var getPrepare = function() { 20 | var deferred = Q.defer(); 21 | deferred.resolve({ 22 | 'cookie': [] 23 | }); 24 | return deferred.promise; 25 | } 26 | var funcs = [ 27 | function(sharedObject) { 28 | var deferred = Q.defer(); 29 | 30 | var options = { 31 | hostname: 'my.illinois.edu', 32 | port: 443, 33 | path: '/uPortal/render.userLayoutRootNode.uP', 34 | method: 'GET', 35 | headers: { 36 | 'Host': 'my.illinois.edu', 37 | 'Upgrade-Insecure-Requests': 1, 38 | 'Cookie': sharedObject.cookie.join(';'), 39 | 'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/29.0.1547.62 Safari/537.36' 40 | } 41 | }; 42 | 43 | console.log('\nREQUEST'.request, options.hostname + options.path); 44 | 45 | var req = https.request(options, function(res) { 46 | console.log('STATUS'.result, res.statusCode); 47 | if (config.debug) { 48 | console.log('HEADERS'.data, res.headers); 49 | } 50 | 51 | res.on('data', function(d) { 52 | // process.stdout.write(d); 53 | }); 54 | 55 | res.on('end', function(){ 56 | util.combineCookies(res.headers['set-cookie'], sharedObject); 57 | // console.log('cookie : ', sharedObject.cookie); 58 | deferred.resolve(sharedObject); 59 | }); 60 | }); 61 | req.end(); 62 | 63 | req.on('error', function(e) { 64 | deferred.reject(e); 65 | // console.error(e); 66 | }); 67 | 68 | return deferred.promise; 69 | }, 70 | 71 | function(sharedObject) { 72 | var deferred = Q.defer(); 73 | 74 | var postData = querystring.stringify({ 75 | 'action': 'login', 76 | 'userName': netID, 77 | 'password': password, 78 | 'Login': 'Sign+In', 79 | }); 80 | 81 | var options = { 82 | jar: true, 83 | hostname: 'my.illinois.edu', 84 | port: 443, 85 | path: '/uPortal/Login', 86 | method: 'POST', 87 | headers: { 88 | 'Host': 'my.illinois.edu', 89 | 'Content-Type': 'application/x-www-form-urlencoded', 90 | 'Content-Length': postData.length, 91 | 'Cookie': sharedObject.cookie.join(';'), 92 | 'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/29.0.1547.62 Safari/537.36' 93 | } 94 | }; 95 | 96 | console.log('\nREQUEST'.request, options.hostname + options.path); 97 | 98 | var req = https.request(options, function(res) { 99 | console.log('STATUS'.result, res.statusCode); 100 | if (config.debug) { 101 | console.log('HEADERS'.data, res.headers); 102 | } 103 | 104 | res.setEncoding('utf8'); 105 | res.on('data', function(chunk) { 106 | // console.log(`BODY: ${chunk}`); 107 | }); 108 | res.on('end', function() { 109 | util.combineCookies(res.headers['set-cookie'], sharedObject); 110 | // console.log('No more data in response.') 111 | deferred.resolve(sharedObject); 112 | }) 113 | }); 114 | 115 | req.on('error', function(e) { 116 | deferred.reject(e); 117 | // console.log(`problem with request: ${e.message}`); 118 | }); 119 | 120 | // write data to request body 121 | req.write(postData); 122 | req.end(); 123 | 124 | return deferred.promise; 125 | }, 126 | 127 | function(sharedObject) { 128 | var deferred = Q.defer(); 129 | 130 | var options = { 131 | hostname: 'my.illinois.edu', 132 | port: 443, 133 | path: '/uPortal/render.userLayoutRootNode.uP?uP_root=root&uP_sparam=activeTabTag&activeTabTag=Academics', 134 | method: 'GET', 135 | headers: { 136 | 'Host': 'my.illinois.edu', 137 | 'Cookie': sharedObject.cookie.join(';'), 138 | 'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/29.0.1547.62 Safari/537.36' 139 | } 140 | }; 141 | 142 | console.log('\nREQUEST'.request, options.hostname + options.path); 143 | 144 | var data = ''; 145 | 146 | var req = https.request(options, function(res) { 147 | console.log('STATUS'.result, res.statusCode); 148 | if (config.debug) { 149 | console.log('HEADERS'.data, res.headers); 150 | } 151 | 152 | var contentLength = res.headers['content-length']; 153 | 154 | res.on('data', function(d) { 155 | data += d; 156 | // process.stdout.write("Downloading " + data.length + " bytes "); 157 | // for (var i = 0; i < 20; i++) { 158 | // if (i / 20 < data.length / contentLength) { 159 | // process.stdout.write("▓"); 160 | // } else { 161 | // process.stdout.write("▓".gray); 162 | // } 163 | // } 164 | // process.stdout.write("\r"); 165 | // process.stdout.write("\r"); 166 | // process.stdout.write(d); 167 | }); 168 | 169 | res.on('end', function() { 170 | util.combineCookies(res.headers['set-cookie'], sharedObject); 171 | // console.log('DATA'.data, data.data); 172 | sharedObject.courseRawData = data; 173 | 174 | deferred.resolve(sharedObject); 175 | }); 176 | }); 177 | 178 | // req.write(postData); 179 | req.end(); 180 | 181 | req.on('error', function(e) { 182 | deferred.reject(e); 183 | }); 184 | 185 | return deferred.promise; 186 | } 187 | ]; 188 | 189 | var result = Q.fcall(getPrepare); 190 | funcs.forEach(function(f) { 191 | result = result.then(f); 192 | }); 193 | 194 | // .done(); 195 | 196 | return result; 197 | } 198 | 199 | const htmlparser = require("htmlparser2"); 200 | 201 | var parseData = function(sharedObject) { 202 | var deferred = Q.defer(); 203 | 204 | var level = 0, 205 | flag = false, 206 | tableLevel = 0, 207 | course = [], 208 | subArray = [], 209 | thirdArray = []; 210 | 211 | var parser = new htmlparser.Parser({ 212 | onopentag: function(name, attribs) { 213 | level++; 214 | if (name.request.indexOf('table') >= 0 && level == 19) { 215 | tableLevel = level; 216 | flag = true; 217 | } 218 | if (flag === true) { 219 | // console.log(level, name.request, attribs); 220 | } 221 | }, 222 | ontext: function(text) { 223 | text = text.replace("\n", ""); 224 | if (flag === true) { 225 | // console.log("-->", text); 226 | switch (level) { 227 | case 20: 228 | if (subArray[0] != null && thirdArray[0] != null) { 229 | subArray.push(thirdArray); 230 | course.push(subArray); 231 | } 232 | subArray = []; 233 | thirdArray = []; 234 | break; 235 | case 21: 236 | if (text !== ' ') subArray.push(text); 237 | break; 238 | case 22: 239 | if (text !== ' ') thirdArray.push(text); 240 | break; 241 | } 242 | } 243 | }, 244 | onclosetag: function(tagname) { 245 | level--; 246 | if (level < tableLevel && flag === true) { 247 | flag = false; 248 | } 249 | // console.log(tagname.green); 250 | }, 251 | onend: function() { 252 | // for (var i = 0; i < tree.length; i++) { 253 | // for (var j in sharedObject['subjectList']) { 254 | // if (sharedObject['subjectList'][j] === tree[i].subject) { 255 | // tree[i].code = j; 256 | // } 257 | // } 258 | // } 259 | // sharedObject.courseTree = tree; 260 | // console.log(sharedObject.courseTree); 261 | sharedObject.course = course; 262 | deferred.resolve(sharedObject); 263 | // console.log(util.inspect(tree, { showHidden: false, depth: null })); 264 | }, 265 | }, { decodeEntities: true }); 266 | parser.write(sharedObject.courseRawData); 267 | parser.end(); 268 | 269 | return deferred.promise; 270 | } 271 | 272 | module.exports = function(netid, password, res) { 273 | return getCourses(netid, password) 274 | .then(parseData) 275 | } 276 | --------------------------------------------------------------------------------