Last active
March 7, 2018 12:05
-
-
Save bathtime/d69b307094ce65c503cc4e6c7aa9ed5b to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// This program checks and/or monitors your emails, | |
// prints a count of new mail, and runs user a defined | |
// command upon a new email. | |
// | |
// Note: A file containing only the users last email | |
// count will be stored your computer in: | |
// | |
// ~/.config/mailcount | |
// | |
// The goals of this project: | |
// | |
// 1. < 100 lines code | |
// 2. Simple & elegant coding | |
// 3. Fast & efficient execution. | |
// | |
// "Do one thing, | |
// and do it well." | |
// | |
// —Linux Credo | |
// | |
// Compile with: | |
// g++ -O -Wall mailnum.cpp -o mailnum -L/usr/include/curl/lib -lcurl -std=gnu++14 | |
// | |
// To check mail and exit: | |
// $ mailnum | |
// | |
// To reset mail count (needed so program doesn't keep alerting | |
// of new mail): | |
// $ mailnum -r | |
// | |
// To continue checking mail every 600 seconds and open | |
// mutt upon a new mail (checks default to every 300 seconds): | |
// $ mailnum -c 600 "mutt" | |
// | |
// To continue checking mail and run the command 'beep' | |
// at every check ('beep' application must be installed): | |
// $ mailnum -c -a beep | |
// | |
#include<experimental/string_view> | |
#include<iostream> | |
#include<fstream> | |
#include<string> | |
#include<curl/curl.h> | |
#include<cstring> | |
#include<algorithm> | |
#include<sys/types.h> | |
#include<pwd.h> | |
#include<unistd.h> | |
#include<chrono> | |
#include<thread> | |
using namespace std; | |
string data; // Will hold the url's contents | |
size_t writeCallback(char* buf, size_t size, size_t nmemb, void* up) | |
{ | |
/* 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 */ | |
for (unsigned int c = 0; c < size*nmemb; c++) | |
data.push_back(buf[c]); | |
return size*nmemb; // Tell curl how many bytes we handled | |
} | |
int main(int argc, char* argv[]) | |
{ | |
// Set defaults if they are not entered as parameters | |
long secsToNextCheck = 300; // Seconds between mail checks (if keepChecking ON) | |
bool keepChecking = 0; // Keep checking mail at intervals? | |
bool isRead = 0; // Are you just telling the program you read the mail? | |
bool keepAlerting = 0; // Repeat new mail command each check? | |
bool hasAlerted = 0; | |
bool showHelp = 0; | |
std::string newMailCmd = ""; // Command to run upon new mail | |
std::string tmpStr; // Needed to help establish if argv is a num | |
// Sort out parameters and set variables | |
for(int pNum = 1; pNum < argc; pNum++) { | |
tmpStr = argv[pNum]; | |
// First check if it is a number. Use new efficient string_view func :) | |
if (!std::experimental::string_view(argv[pNum]).empty() && std::all_of(tmpStr.begin(), tmpStr.end(), ::isdigit)) | |
secsToNextCheck = stoi(argv[pNum]); | |
else if (std::experimental::string_view(argv[pNum]) == "-r") | |
isRead = 1; | |
else if (std::experimental::string_view(argv[pNum]) == "-a") | |
keepAlerting = 1; | |
else if (std::experimental::string_view(argv[pNum]) == "-c") | |
keepChecking = 1; | |
else if (std::experimental::string_view(argv[pNum]) == "-h") | |
showHelp = 1; | |
else{ // Remaining parameters must be the command to run | |
/* User didn't put alert command in quotations, so add a space | |
before each command to space properly. */ | |
if (newMailCmd != "") | |
newMailCmd += " "; | |
newMailCmd += argv[pNum]; | |
} | |
} | |
if (showHelp) { | |
std::cout << "mailnum v0.1 2018\n\n" | |
"This program checks and monitors your new email count, " | |
"prints a count of new mail, and executes a user defined " | |
"command upon a new email.\n\n" | |
"mailnum [- options ...] [check freq. in seconds] [command args]\n\n" | |
"Options include:\n" | |
"[ -r ] Mark as read\n" | |
"[ -c ] Check mail continuously\n" | |
"[ -a ] Repeat mail alert command\n" | |
"[ -h ] Print this text\n"; | |
return 0; | |
} | |
const char * charNewMailCmd = newMailCmd.c_str(); // system() won't accept a string | |
std::chrono::milliseconds timespan(secsToNextCheck * 1000); // secs -> millisecs | |
// Find user's home directory to store mail count | |
struct passwd *pw = getpwuid(getuid()); // Set up to get ~/ | |
std::string homeDir = pw->pw_dir; | |
std::string fileName = homeDir + "/.config/mailcount"; | |
// Keep looping until the definition of '1' is no longer equal to 'true' | |
while (1) { | |
CURL* curl; // Our curl object | |
curl_global_init(CURL_GLOBAL_ALL); | |
curl = curl_easy_init(); | |
// Email server information: | |
curl_easy_setopt(curl, CURLOPT_USERNAME, "[email protected]"); | |
curl_easy_setopt(curl, CURLOPT_PASSWORD, "myPasswordGoesHere" ); | |
curl_easy_setopt(curl, CURLOPT_URL, "imaps://mail.myEmailProvider.com:993/INBOX" ); | |
curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "EXAMINE INBOX" ); | |
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, &writeCallback); | |
// May be used to assist in debugging | |
// curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L); // Tell curl to output its progress | |
curl_easy_perform(curl); | |
std::size_t pos = data.find("UIDNEXT"); | |
std::string trueCount = data.substr(pos + 8, 3); | |
data = ""; | |
// Don't want any leaks! | |
curl_easy_cleanup(curl); | |
curl_global_cleanup(); | |
// Email count established above, so compare with file system count | |
std::string lastCount; | |
ifstream testFile(fileName); | |
// Check if file exists or contains data | |
if (testFile.fail()) | |
{ | |
// Make .config if not found | |
tmpStr = "mkdir -p " + homeDir + "/.config/"; | |
const char * mkDirCharCmd = tmpStr.c_str(); // system() won't take a string | |
system(mkDirCharCmd); | |
testFile.close(); | |
std::ofstream file(fileName); | |
file << trueCount; | |
file.close(); | |
lastCount = trueCount; | |
}else{ // The file is there, so test its contents! | |
getline (testFile,lastCount); | |
testFile.close(); | |
// Check if content is a number or empty | |
if (lastCount.empty() || !std::all_of(lastCount.begin(), lastCount.end(), ::isdigit)) | |
{ | |
std::ofstream file(fileName); | |
file << trueCount; | |
file.close(); | |
lastCount = trueCount; | |
} | |
} | |
testFile.close(); | |
int newMail = stoi(trueCount) - stoi(lastCount); | |
if (newMail || isRead) | |
{ | |
if (isRead) // We saw it, so update lastCount and exit | |
{ | |
std::ofstream file(fileName); | |
file << trueCount; | |
file.close(); | |
if (!newMail) | |
std::cout << "No new mail to mark as read." << endl; | |
else | |
std::cout << "Mail marked as read." << endl; | |
return 0; // No need to stick around! | |
}else{ | |
std::cout << "[" << newMail << " new email"; | |
// Append an 's' if +1 emails | |
if (newMail > 1) | |
std::cout << "s] " << endl; | |
else | |
std::cout << "] " << endl; | |
if (!hasAlerted) { | |
system(charNewMailCmd); | |
hasAlerted = 1; | |
} else if (keepAlerting) | |
system(charNewMailCmd); | |
} | |
} | |
if (keepAlerting && !keepChecking) | |
std::cout << "Cannot keep alerting when set to check once." << endl; | |
// Wait until next mail check (convert to milliseconds) | |
if (keepChecking) | |
std::this_thread::sleep_for(timespan); | |
else | |
return 0; | |
} | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment