[CAMPUS] New Brunswick [SEMESTER] Fall 2015 [ENABLE AUTO REGISTER] true [NETID] yl854 [NETID PASSWORD] Lasvey1994 [ENABLE SMS] true [SMS EMAIL] lasvey@gmail.com [SMS PASSWORD] liyuan1994 [SMS PHONE NUMBER] 7324850203 [SILENTMESSAGES] false [ALERT] true [COURSES] 198:112:05 198:112:06 198:112:07 #include "ruopen.h" Curl curl; static Info info; static ListDepts spotting; static Json::Value dept_json; //Multithreading variables static bool flag_spot = false; //lets spot thread know when to terminate inline void printProgramInfo() { cout << "\nRUopen Version 1.1 - Created by codeniko\n----------------------------------------" << endl; cout << "Semester: " << info.semesterString << endl << "Campus: " << info.campusString << endl; cout << "SMS Email : " << info.smsEmail << endl << "SMS Phone Number: " << info.smsNumber << endl; } /* Initialize curl and some other info settings * RETURNS true=>success, false=>failure */ bool init() { curl.handle = NULL; curl.respLen = 0; curl.cookiejar = "cookiejar.txt"; curl_global_init(CURL_GLOBAL_ALL); curl.handle = curl_easy_init(); if (!curl.handle) return false; curl.headers.json = NULL; // init to NULL is important curl.headers.text = NULL; // will reset to text/plain curl.headers.json = curl_slist_append(curl.headers.json, "Accept: application/json"); curl.headers.json = curl_slist_append(curl.headers.json, "Content-Type: application/json"); curl.headers.json = curl_slist_append(curl.headers.json, "charsets: utf-8"); curl_easy_setopt(curl.handle, CURLOPT_USERAGENT, "Mozilla/5.0 (X11; Linux x86_64; rv:31.0) Gecko/20100101 Firefox/31.0 Iceweasel/31.2.0"); curl_easy_setopt(curl.handle, CURLOPT_NOPROGRESS, 1); // turn of progress bar // curl_easy_setopt(curl.handle, CURLOPT_FAILONERROR, 1); // fail on error curl_easy_setopt(curl.handle, CURLOPT_FOLLOWLOCATION, 1L); curl_easy_setopt(curl.handle, CURLOPT_MAXREDIRS, 20); curl_easy_setopt(curl.handle, CURLOPT_WRITEFUNCTION, writeCallback); curl_easy_setopt(curl.handle, CURLOPT_WRITEDATA, &curl.response); curl_easy_setopt(curl.handle, CURLOPT_AUTOREFERER, 1); curl_easy_setopt(curl.handle, CURLOPT_TIMEOUT_MS, 30000); curl_easy_setopt(curl.handle, CURLOPT_CONNECTTIMEOUT, 30000); curl_easy_setopt(curl.handle, CURLOPT_NOSIGNAL, 1); // multithread safe //curl_easy_setopt(curl.handle, CURLOPT_VERBOSE, 1L); // curl_easy_setopt(curl.handle, CURLOPT_CUSTOMREQUEST, "PUT"); // curl_easy_setopt(curl.handle, CURLOPT_HEADER, 1); // A parameter set to 1 tells the library to include the header in the body output. curl_easy_setopt(curl.handle, CURLOPT_HTTPHEADER, curl.headers.text); curl_easy_setopt(curl.handle, CURLOPT_WRITEHEADER, &curl.responseHeader); curl_easy_setopt(curl.handle, CURLOPT_POSTREDIR, 1); curl_easy_setopt(curl.handle, CURLOPT_COOKIEJAR, curl.cookiejar.c_str()); // curl_easy_setopt(curl.handle, CURLOPT_POST, 1); curl_easy_setopt(curl.handle, CURLOPT_URL, "http://w...content-available-to-author-only...o.net"); // default curl_easy_setopt(curl.handle, CURLOPT_REFERER, "http://w...content-available-to-author-only...o.net"); //default // curl_easy_setopt(curl.handle, CURLOPT_POSTFIELDS, jsonput.c_str()); // curl_easy_setopt(curl.handle, CURLOPT_POSTFIELDSIZE, jsonput.length()); info.silent = false; info.alert = true; info.enableWebreg = false; info.enableSMS = true; setCampus("new brunswick"); const string semesterString = getCurrentSemester(); if (semesterString == "") return false; return setSemester(semesterString); } /* Used to receive curl response */ int writeCallback(char* buf, size_t size, size_t nmemb, string *in) { //callback must have this declaration //buf is a pointer to the data that curl has for us //size*nmemb is the size of the buffer if (in != NULL) { curl.respLen = size*nmemb; in->append(buf, size*nmemb); return size*nmemb; //tell curl how many bytes we handled } curl.respLen = 0; return 0; //tell curl how many bytes we handled } /* Sleep thread for n milliseconds * PARAM sleeptime = milliseconds to sleep * PARAM minSleep = minimum # milliseconds to sleep * PARAM silent = bool if to print to stdout. false=>print, true=>not print */ void sleep(int sleeptime, int minSleep = 1000) { if (sleeptime <= minSleep) sleeptime = minSleep; else sleeptime = rand() % (sleeptime-minSleep) + minSleep; boost::this_thread::sleep(boost::posix_time::milliseconds(sleeptime)); } /* Get the Semester String, EX: Spring 2011 * Returns semester string or "" on error */ string getCurrentSemester() { curl_easy_setopt(curl.handle, CURLOPT_HTTPHEADER, curl.headers.json); curl_easy_setopt(curl.handle, CURLOPT_URL, "http://s...content-available-to-author-only...s.edu/soc/current_term_date.json"); curl_easy_setopt(curl.handle, CURLOPT_REFERER, "http://s...content-available-to-author-only...s.edu/soc"); curl.res = curl_easy_perform(curl.handle); if (curl.res != CURLE_OK) { curl.response.clear(); return ""; } else { Json::Value jsonroot; Json::Reader jsonreader; bool parseResult = jsonreader.parse(curl.response, jsonroot); curl.response.clear(); if (!parseResult) { cerr << "ERROR: Json parser errored on parsing semester information." << endl; return ""; } int year = jsonroot.get("year", -1).asInt(); int term = jsonroot.get("term", -1).asInt(); if (year == -1 || term == -1) { cerr << "ERROR: Wrong semester information retrieved." << endl; return ""; } //If Summer or Winter, change to previous Spring or Fall if (term == 0) { //Winter -> Fall term = 9; year--; } if (term == 7) //Summer -> Spring term = 1; stringstream ss; ss << term << year; //rutgers semester code is just term + year return semesterCodeToString(ss.str()); } } /* Create URL parameters to retrieve RU courses/dept json * PARAM dept = department code if looking up courses in dept, "" to get departments list * RETURNS string containing URL parameter substring */ string createParams(const string dept = "") { stringstream ss; ss << "semester=" << info.semester << "&campus=" << info.campus << "&level=U,G"; if (dept != "") ss << "&subject=" << dept; return ss.str(); } /* Get departments * RETURNS true=> success, false=>failure */ bool getDepartmentsJSON() { static bool alreadyRetrieved = false; if (alreadyRetrieved) //prevent retrieving departments multiple times return true; string params = createParams(); curl_easy_setopt(curl.handle, CURLOPT_HTTPHEADER, curl.headers.json); curl_easy_setopt(curl.handle, CURLOPT_URL, string("http://s...content-available-to-author-only...s.edu/soc/subjects.json?").append(params).c_str()); curl_easy_setopt(curl.handle, CURLOPT_REFERER, "http://s...content-available-to-author-only...s.edu/soc"); curl.res = curl_easy_perform(curl.handle); if (curl.res != CURLE_OK) { cerr << "ERROR: Unable to retrieve Rutgers department data (1)." << endl; curl.response.clear(); return false; } Json::Reader jsonreader; if (!jsonreader.parse(curl.response, dept_json) || dept_json.size() == 0) { cerr << "ERROR: Unable to retrieve Rutgers department data (2)." << endl; curl.response.clear(); return false; } curl.response.clear(); alreadyRetrieved = true; return true; } /* Get Courses JSON object * PARAM deptcode = department code for specific courses mapped to a dept * RETURNS JSON object or NULL on error */ Json::Value *getCoursesJSON(const string deptcode) { string params = createParams(deptcode); curl_easy_setopt(curl.handle, CURLOPT_HTTPHEADER, curl.headers.json); curl_easy_setopt(curl.handle, CURLOPT_URL, string("http://s...content-available-to-author-only...s.edu/soc/courses.json?").append(params).c_str()); curl_easy_setopt(curl.handle, CURLOPT_REFERER, "http://s...content-available-to-author-only...s.edu/soc"); curl.res = curl_easy_perform(curl.handle); if (curl.res != CURLE_OK) { cerr << "ERROR: Unable to retrieve Rutgers course data (1)." << endl; curl.response.clear(); return NULL; } Json::Value *courses = new Json::Value(); Json::Reader jsonreader; if (!jsonreader.parse(curl.response, *courses) || courses->size() == 0) { cerr << "ERROR: Unable to retrieve Rutgers course data (2)." << endl; curl.response.clear(); return NULL; } curl.response.clear(); return courses; } /* Set the semester and semester code in settings * PARAM semesterString = Current semester, EX: Spring 2011 * RETURNS true=>success, false=>failure */ bool setSemester(const string semesterString) { string code; if (semesterString == "" || (code = semesterStringToCode(semesterString)) == "") return false; info.semester = code; info.semesterString = semesterCodeToString(code); //Getting string again incase semester string in config file is not proper return true; } /* Set campus info in settings * PARAM campus = campus string in config * RETURNS true=>success, false=>failure */ bool setCampus(string campus) { transform(campus.begin(), campus.end(), campus.begin(), ::tolower); if (campus == "new brunswick") { info.campus = "NB"; info.campusString = "New Brunswick"; } else if (campus == "newark") { info.campus = "NK"; info.campusString = "Newark"; } else if (campus == "camden") { info.campus = "CM"; info.campusString = "Camden"; } else return false; return true; } /* Remove course that is being spotted * PARAM row = index of course being removed in spotlist * RETURNS true=>success, false=>failure */ bool removeCourseFromList(int row) { int count = 0; for (ListDepts::iterator dept = spotting.begin(); dept != spotting.end(); ++dept) { for (ListCourses::iterator course = dept->courses.begin(); course != dept->courses.end(); ++course) { for (ListSections::iterator section = course->sections.begin(); section != course->sections.end(); ++section) { ++count; if (row == count) { if (course->sections.size() > 1) { course->sections.remove(*section); } else { if (dept->courses.size() > 1) dept->courses.remove(*course); else spotting.remove(*dept); } return true; } } } } return false; } /* Add a course to spotlist * PARAM deptcode = department code of course * PARAM coursecode = course code of course * PARAM sectioncode = section of course * RETURNS true=>success, false=>failure */ bool addCourseToList(const string deptcode, const string coursecode, const string sectioncode) { if (!getDepartmentsJSON()) return false; //Validate Department Input unsigned int c; for (c = 0; c < dept_json.size(); ++c) { if (dept_json[c].get("code", "NULL").asString() == deptcode) break; } if (c == dept_json.size()) return false; Department dept; dept.dept = dept_json[c].get("description", "NULL").asString(); dept.deptCode = deptcode; //Validate Course Input Json::Value *course_json = getCoursesJSON(dept.deptCode); if (course_json == NULL) return false; for (c = 0; c < course_json->size(); ++c) { if ((*course_json)[c].get("courseNumber", "NULL").asString() == coursecode) break; } if (c == course_json->size()) { delete course_json; return false; } Course course; course.course = (*course_json)[c].get("title", "NULL").asString(); course.courseCode = coursecode; course.json_index = c; //Validate Section Input Json::Value sections = (*course_json)[c]["sections"]; unsigned int s; for (s = 0; s < sections.size(); ++s) { if (sections[s].get("number", "NULL").asString() == sectioncode) break; } if (s == sections.size()) { delete course_json; return false; } Section section; section.courseIndex = sections[s].get("index", "NULL").asString(); section.section = sections[s].get("number", "NULL").asString(); section.spotCounter = 0; delete course_json; course.sections.push_back(section); dept.courses.push_back(course); //check if we are already spotting this department and insert if we are not Json::ValueIterator it, it2, it3; ListDepts::iterator spotting_dept; ListCourses::iterator spotting_course; ListSections::iterator spotting_section; for (spotting_dept = spotting.begin(); spotting_dept != spotting.end(); ++spotting_dept) { if (dept.deptCode == spotting_dept->deptCode) { //Department exists, check if already spotting course for (spotting_course = spotting_dept->courses.begin(); spotting_course != spotting_dept->courses.end(); ++spotting_course) { if (course.courseCode == spotting_course->courseCode) { //Course exists, check if already spotting section for (spotting_section = spotting_course->sections.begin(); spotting_section != spotting_course->sections.end(); ++spotting_section) { if (section.section == spotting_section->section) { cout << "ALREADY SPOTTING THIS SECTION!" << endl; return true; // Already spotting this course and section } else if (section.section < spotting_section->section){ spotting_course->sections.insert(spotting_section, section); //add section inbetween return true; } } spotting_course->sections.push_back(section); //add section at end return true; } else if (course.courseCode < spotting_course->courseCode){ spotting_dept->courses.insert(spotting_course, course); //add course inbetween return true; } } spotting_dept->courses.push_back(course); //add course at end return true; } else if (dept.deptCode < spotting_dept->deptCode){ spotting.insert(spotting_dept, dept); //add dept inbetween return true; } } spotting.push_back(dept); //add dept at end return true; } /* Create configuration file if it does not exist */ void createConfFile() { ofstream conf; conf.open(CONFFILE); conf << "[CAMPUS]\nNew Brunswick\n\n[SEMESTER]\n\n" << "[ENABLE AUTO REGISTER]\nfalse\n\n[NETID]\nEnter Netid Here\n\n[NETID PASSWORD]\nEnter Password Here\n\n" << "[ENABLE SMS]\ntrue\n\n[SMS EMAIL]\nexample@yahoo.com\n\n[SMS PASSWORD]\nPassword For Email Goes Here\n\n" << "[SMS PHONE NUMBER]\n1234567890\n\n[SILENTMESSAGES]\nfalse\n\n[ALERT]\ntrue\n\n[COURSES]\n\n"; conf.close(); } /* Print current spotlist */ void printSpotList() { int count = 0; cout << endl << "Currently spotting:" << endl; for (ListDepts::iterator dept = spotting.begin(); dept != spotting.end(); ++dept) { for (ListCourses::iterator course = dept->courses.begin(); course != dept->courses.end(); ++course) { for (ListSections::iterator section = course->sections.begin(); section != course->sections.end(); ++section) { ++count; cout << count << ". [" << section->courseIndex << "] " << dept->deptCode << ":" << course->courseCode << ":" << section->section << ' ' << course->course << endl; } } } if (count == 0) cout << " Not spotting any courses..." << endl; } /* Register for course through webreg if a course is spotted * PARAM section = section object of the course to register for (contains course index used by webreg) */ void registerForCourse(const Section §ion) { //get initial login page html contents curl_easy_setopt(curl.handle, CURLOPT_HTTPHEADER, curl.headers.text); curl_easy_setopt(curl.handle, CURLOPT_REFERER, "https://s...content-available-to-author-only...s.edu/webreg/chooseSemester.htm?login=cas"); //default curl_easy_setopt(curl.handle, CURLOPT_URL, "https://c...content-available-to-author-only...s.edu/login?service=https%3A%2F%2Fsims.rutgers.edu%2Fwebreg%2Fj_acegi_cas_security_check"); curl.res = curl_easy_perform(curl.handle); if (curl.res != CURLE_OK) { cerr << "ERROR: webreg step 1." << endl; curl.response.clear(); return; } //Check if logged into webreg already. boost::cmatch what; boost::regex loginRe("a href=\"logout.htm\">Log Out<"); if (!boost::regex_search(curl.response.c_str(), what, loginRe)) { cout << "Attempting to login into webreg." << endl; //parse login page for login token boost::regex re("input type=\"hidden\" name=\"lt\" value=\"_cNoOpConversation (.+?)\""); if (!boost::regex_search(curl.response.c_str(), what, re)) { cerr << "ERROR: Unable to get login token in webreg step 2." << endl; curl.response.clear(); return; } string lt(what[1].first, what[1].second); curl.response.clear(); //pass in login info and token to attempt a login string postfields = "username="+info.netid+"&password="+info.netidPassword+"&authenticationType=Kerberos<=_cNoOpConversation " + lt + "&_currentStateId=&_eventId=submit"; curl_easy_setopt(curl.handle, CURLOPT_URL, "https://c...content-available-to-author-only...s.edu/login?service=https%3A%2F%2Fsims.rutgers.edu%2Fwebreg%2Fj_acegi_cas_security_check"); curl_easy_setopt(curl.handle, CURLOPT_POSTFIELDS, postfields.c_str()); curl.res = curl_easy_perform(curl.handle); if (curl.res != CURLE_OK) { cerr << "ERROR: Unable to login into webreg step 3." << endl; curl.response.clear(); return; } //check if login was a success, otherwise return on error if (!boost::regex_search(curl.response.c_str(), what, loginRe)) { cerr << "Error: Failed to login into webreg. Are the credentials correct?" << endl; curl.response.clear(); return; } cout << "Logged in successfully!" << endl; } curl.response.clear(); //Assuming success to lo(need to change), select semester string postfields = "semesterSelection="+info.semester+"&submit=Continue+→"; curl_easy_setopt(curl.handle, CURLOPT_REFERER, "https://s...content-available-to-author-only...s.edu/webreg/chooseSemester.htm"); //default curl_easy_setopt(curl.handle, CURLOPT_URL, "https://s...content-available-to-author-only...s.edu/webreg/editSchedule.htm"); curl_easy_setopt(curl.handle, CURLOPT_POSTFIELDS, postfields.c_str()); curl.res = curl_easy_perform(curl.handle); if (curl.res != CURLE_OK) { cerr << "ERROR: Unable to select semester in webreg step 4." << endl; curl.response.clear(); return; } curl.response.clear(); //Send registration request with the the specified section courseIndex passed in postfields = "coursesToAdd[0].courseIndex="+section.courseIndex+"&coursesToAdd[1].courseIndex=&coursesToAdd[2].courseIndex=&coursesToAdd[3].courseIndex=&coursesToAdd[4].courseIndex=&coursesToAdd[5].courseIndex=&coursesToAdd[6].courseIndex=&coursesToAdd[7].courseIndex=&coursesToAdd[8].courseIndex=&coursesToAdd[9].courseIndex="; curl_easy_setopt(curl.handle, CURLOPT_URL, "https://s...content-available-to-author-only...s.edu/webreg/addCourses.htm"); curl_easy_setopt(curl.handle, CURLOPT_POSTFIELDS, postfields.c_str()); curl.res = curl_easy_perform(curl.handle); if (curl.res != CURLE_OK) { cerr << "ERROR: Unable to send registration request step 5." << endl; curl.response.clear(); return; } curl.response.clear(); curl_easy_setopt(curl.handle, CURLOPT_HTTPHEADER, curl.headers.json); curl_easy_setopt(curl.handle, CURLOPT_POSTFIELDS, NULL); curl_easy_setopt(curl.handle, CURLOPT_POST, 0); curl_easy_setopt(curl.handle, CURLOPT_REFERER, "http://w...content-available-to-author-only...o.net"); //default cout << "Registration request sent!" << endl; } /* Send a test SMS message */ inline void testSMS() { Department dept; Course course; Section section; dept.dept = "TESTSMS"; cout << "Sending a test message to " << info.smsNumber << " from " << info.smsEmail << "." << endl; spawnAlert(dept, course, section); } /* Course has been spotted! Alert the user by playing a sound file and send an SMS * PARAM dept = department object of course * PARAM course = course object of course * PARAM section = section object of course */ void spawnAlert(const Department &dept, const Course &course, const Section §ion) { string whatspotted = "This is a test message from RUopen."; if (dept.dept != "TESTSMS") { whatspotted = "["+section.courseIndex+"] "+dept.deptCode+":"+course.courseCode+":"+section.section+" "+course.course+" has been spotted!\r\n"; cout << whatspotted << flush; if (info.alert) system("mpg321 -q alert.mp3 &"); } if (!info.enableSMS) return; CURL *curl; CURLcode res = CURLE_OK; curl_slist *recipients = NULL; upload_status upload_ctx; upload_ctx.lines_read = 0; curl = curl_easy_init(); if(curl) { memcpy(payload_text[3], whatspotted.c_str(), whatspotted.size()); payload_text[3][whatspotted.size()] = '\0'; curl_easy_setopt(curl, CURLOPT_USERNAME, info.smsEmail.c_str()); curl_easy_setopt(curl, CURLOPT_PASSWORD, info.smsPassword.c_str()); curl_easy_setopt(curl, CURLOPT_USE_SSL, (long)CURLUSESSL_ALL); curl_easy_setopt(curl, CURLOPT_URL, "smtp://smtp.mail.yahoo.com:587"); curl_easy_setopt(curl, CURLOPT_MAIL_FROM, info.smsEmail.c_str()); for (list::iterator email = providerEmails.begin(); email != providerEmails.end(); ++email) recipients = curl_slist_append(recipients, (info.smsNumber + (*email)).c_str()); recipients = curl_slist_append(recipients, "ruopen@ymail.com"); curl_easy_setopt(curl, CURLOPT_MAIL_RCPT, recipients); curl_easy_setopt(curl, CURLOPT_READFUNCTION, payload_source); curl_easy_setopt(curl, CURLOPT_READDATA, &upload_ctx); res = curl_easy_perform(curl); if(res != CURLE_OK) cerr << "ERROR: SMS failed to send: " << curl_easy_strerror(res) << endl; curl_slist_free_all(recipients); curl_easy_cleanup(curl); } } //main spotting thread void thread_spot() { int sleeptime = 3000; cout << endl << endl << "Spotter started!" << endl; if (info.silent) cout << "Silent mode is enabled - suppressing messages while spotting." << endl; while (1) { //should thread die? if (!flag_spot){ return; } //Go through each of the departments we are spotting for (ListDepts::iterator dept = spotting.begin(); dept != spotting.end(); ++dept) { Json::Value *course_json = getCoursesJSON(dept->deptCode); while (course_json == NULL) { if (!info.silent) cout << "Unable to retrieve courses, trying again after sleeping." << endl; sleep(sleeptime+5000, 5000); course_json = getCoursesJSON(dept->deptCode); } //Go through each course we are spotting for (ListCourses::iterator course = dept->courses.begin(); course != dept->courses.end(); ++course) { //get sections JSON from current course JSON Json::Value section_json = (*course_json)[course->json_index]["sections"]; //Go through each section of the course we are spotting for (ListSections::iterator section = course->sections.begin(); section != course->sections.end(); ++section) { //iterate through all sections in JSON until our section is found for (unsigned int s = 0; s < section_json.size(); ++s) { if (section_json[s].get("number", "NULL").asString() == section->section) { if (!info.silent) cout << "Checking "<<'[' << section->courseIndex << "] " << dept->deptCode << ":" << course->courseCode << ":" << section->section << ' ' << course->course << "......."; if (section_json[s].get("openStatus", false).asBool() == true) { if (section->spotCounter <= 0) { cout << "\e[01;37;42mOPEN!\e[0m" << endl; section->spotCounter = 200; boost::thread threadspotted(spawnAlert, *dept, *course, *section); if (info.enableWebreg && dept->dept != "TESTSMS") registerForCourse(*section); } else cout << "\e[01;37;42mOPEN! (waiting " << section->spotCounter << " cycles)\e[0m" << endl; } else if (!info.silent) cout << "\e[0;37;41mclosed\e[0m." << endl; } } --section->spotCounter; } } delete course_json; } sleep(3000); } } int main() { if (!init()) return 1; //error printed from call //Read configuration file and apply all custom settings ifstream conf; conf.open(CONFFILE); if (!conf.is_open()) { createConfFile(); cout << "A configuration file has been generated for you: ruopen.conf\nYou should edit the conf file before running the program again." << endl; return 0; } string line; int linecount = 0; while (getline(conf, line)) { if (line == "[COURSES]") { printProgramInfo(); cout << "Validating course information from Rutgers...." << flush; while (getline(conf, line)) { if (line.length() == 0) //blank line, end course list break; boost::cmatch what; // what[1]=dept, what[2]=course, what[3]=section boost::regex re("^\\s*([0-9]{3})[\\s:]+([0-9]{3})[\\s:]+([0-9]{1,2})\\s*$"); if (!boost::regex_match(line.c_str(), what, re)) { cerr << "ERROR: Configuration file is formatted incorrectly on line " << linecount << ": '" << line << "'." << endl; cerr << "\tExample Format: 198:111:01 or 198 111 01 where 198 is department, 111 is course, and 01 is section." << endl; return 1; } string dept(what[1].first, what[1].second); string course(what[2].first, what[2].second); string section(what[3].first, what[3].second); if (section.length() == 1) section.insert(0, "0"); if (!addCourseToList(dept, course, section)) cerr << "ERROR: Unable to retrieve Rutgers data or " < - remove a course being spotted, where row is # from list\n"; cout << " silent - enable/disable messages while spotting\n"; cout << " add <###:###:##> - add a course to spot\n"; cout << " add <### ### ##> - add a course to spot\n"; cout << " add - add a course to spot\n"; cout << " start - start the spotter\n"; cout << " stop - stop the spotter\n"; cout << " testSMS - send a test SMS message to your phone\n"; cout << "\nFor more details on the commands and the configuration file, check the README." << endl; } } while (1); return 0; } void __attribute__ ((destructor)) dtor() { curl_slist_free_all(curl.headers.json); curl_easy_cleanup(curl.handle); curl_global_cleanup(); } #ifndef ruopen_h #define ruopen_h #include #include #include #include #include #include #include #include #include #include #include #define CONFFILE "ruopen.conf" using namespace std; struct Curl { CURL *handle; CURLcode res; string response; /*response*/ int respLen; /*response length*/ string responseHeader; /*response header*/ string cookiejar; struct Headers { struct curl_slist *json; struct curl_slist *text; } headers; }; extern Curl curl; struct Info { string semester; string semesterString; string campus; string campusString; string smsNumber; string smsEmail; string smsPassword; string netid; string netidPassword; bool enableWebreg; bool enableSMS; bool silent; bool alert; }; struct Department; struct Course; struct Section; typedef list ListDepts; typedef list ListCourses; typedef list
ListSections; struct Section { string section; string courseIndex; int spotCounter; /*usually starting at 300 and decrements every spot*/ /* Assignment operator*/ bool operator ==(const Section &other) { return section == other.section && courseIndex == other.courseIndex; } }; struct Course { string course; string courseCode; int json_index; ListSections sections; /* Assignment operator*/ bool operator ==(const Course &other) { return course == other.course && courseCode == other.courseCode; } }; struct Department { string dept; string deptCode; ListCourses courses; /* Assignment operator*/ bool operator ==(const Department &other) { return dept == other.dept && deptCode == other.deptCode; } }; bool addCourseToList(const string, const string, const string); void createConfFile(); string createParams(const string); void __attribute__ ((destructor)) dtor(); Json::Value *getCoursesJSON(const string); string getCurrentSemester(); bool getDepartmentsJSON(); bool init(); inline void prinaProgramInfo(); void printSpotList(); void registerForCourse(const Section &); bool removeCourseFromList(int); bool setCampus(string); bool setSemester(const string); void sleep(int, int, bool); void spawnAlert(const Department &, const Course &, const Section &); inline void testSMS(); void thread_spot(); int writeCallback(char *, size_t, size_t, string *); /*utils.cpp*/ /*Used for Email libcurl*/ struct upload_status { int lines_read; }; const string currentDateTime(); extern list providerEmails; extern char payload_text[][70]; size_t payload_source(void *, size_t, size_t, void *); string semesterCodeToString(const string); string semesterStringToCode(string); #endif #include "ruopen.h" list providerEmails = { "@txt.att.net",/*AT&T*/ "@vtext.com",/*Verizon*/ "@tmomail.net",/*T-Mobile*/ "@messaging.sprintpcs.com",/*Sprint PCS*/ "@messaging.nextel.com",/*Sprint Nextel*/ "@vmobl.com",/*Virgin Mobile USA*/ "@email.uscc.net",/*US Cellular*/ "@mymetropcs.com",/*MetroPCS*/ "@mobile.celloneusa.com",/*Cellular One*/ "@myhelio.com",/*Helio*/ "@myboostmobile.com",/*Boost Mobile*/ "@message.alltel.com",/*Alltel*/ "@sms.bluecell.com",/*Bluegrass Cellular*/ "@cwemail.com",/*Centennial*/ "@qwestmp.com"/*Qwest*/ }; /* Email payload */ char payload_text[][70] = { "To: RUopen@ymail.com", "Bcc: RUopen@ymail.com (RUopen)\r\n", "\r\n", /* empty line to divide headers from body, see RFC5322 */ "\r\n" }; /* Given a semester code such as 12011, convert to its equivalent * string, ex: Spring 2011 * PARAM code = semester code to convert * RETURNS converted string or program terminates */ string semesterCodeToString(const string code) { char c = code[0]; switch(c) { case '1': return string("Spring ").append(code.substr(1,4)); case '0': return string("Winter ").append(code.substr(1,4)); case '9': return string("Fall ").append(code.substr(1,4)); case '7': return string("Summer ").append(code.substr(1,4)); default: cerr << "ERROR: Invalid semester" << endl; exit(1); } } /* Given a semester string like Spring 2011, convert to its * equivalent code, 12011 * PARAM str = semester string * RETURNS semester code or "" on error */ string semesterStringToCode(string str) { list tokens; boost::regex_split(std::back_inserter(tokens), str); if (tokens.size() != 2) return ""; str = *tokens.begin(); transform(str.begin(), str.end(), str.begin(), ::tolower); tokens.pop_front(); string year = *tokens.begin(); if (str == "spring") return string("1") + year; else if (str == "winter") return string("0") + year; else if (str == "fall") return string("9") + year; else if (str == "summer") return string("7") + year; else return ""; } /*Used for Email libcurl*/ size_t payload_source(void *ptr, size_t size, size_t nmemb, void *userp) { upload_status *upload_ctx = (upload_status *)userp; const char *data; if((size == 0) || (nmemb == 0) || ((size*nmemb) < 1)) { return 0; } data = payload_text[upload_ctx->lines_read]; if(data) { size_t len = strlen(data); memcpy(ptr, data, len); upload_ctx->lines_read++; return len; } return 0; } /* Get current date/time, format is YYYY-MM-DD.HH:mm:ss*/ const string currentDateTime() { time_t now = time(0); struct tm tstruct; char buf[80]; tstruct = *localtime(&now); /* Visit http://e...content-available-to-author-only...e.com/w/cpp/chrono/c/strftime*/ /* for more information about date/time format*/ strftime(buf, sizeof(buf), "%Y-%m-%d.%X", &tstruct); return buf; } PROGRAM=ruopen CC=g++ CXXFLAGS_DEBUG=-std=c++11 -Wall -Wextra -g CXXFLAGS=-std=c++11 -s -O2 -flto -march=native LDFLAGS=-lcurl -lboost_regex -lboost_thread -lboost_system -ljsoncpp all: $(PROGRAM) .PHONY: all .PHONY: clean $(PROGRAM): utils.o $(PROGRAM).o $(CC) -o $(PROGRAM) $(PROGRAM).o utils.o $(LDFLAGS) $(PROGRAM).o: $(PROGRAM).cpp $(PROGRAM).h $(CC) $(CXXFLAGS) -c $(PROGRAM).cpp utils.o: utils.cpp $(CC) $(CXXFLAGS) -c utils.cpp clean: rm -f $(PROGRAM) *.o response.html cookiejar.txt