Do you have:
[ ] A working Linux OS?
[ ] 30,952kb of space to spare on your hard drive?
[ ] An additional 2mb of ram for this program to use?
[ ] A cranium with matter betwixt it?
If you answered yes to all of the above, you can run this application (aside from given things such as having the required programs on your machine to compile this application yourself, which are, naturally, beyond the scope of this tutorial).
Introducing...
...wait for it...
...wait for it...
...waaaiiiiiiiitttttt...
... ... ...
!! M A I L N U M !!
Okay, the name isn't the most exciting. ... This program will allow you to check your email easily and efficiently and will run a user-specified command upon a new email.
You will need to change this information for it to run:
Code: Select all
curl_easy_setopt(curl, CURLOPT_USERNAME, "yourUserName@yourEmail.com");
curl_easy_setopt(curl, CURLOPT_PASSWORD, "yourPasswordReplacesThisText");
curl_easy_setopt(curl, CURLOPT_URL, "imaps://mail.yourMailServer.com:993/INBOX");
Code: Select all
$ g++ -O -Wall mailnum.cpp -o mailnum -L/usr/include/curl/lib -lcurl -std=gnu++14
Code: Select all
$ sudo apt install build-essential
Here are some examples of usage:
Check if there is new mail and print count if there is, else, don't print anything:
Code: Select all
$ mailnum
Code: Select all
[1 new email]
Yes, to have it check every 300 seconds (default), run:
Code: Select all
$ mailnum -c
Okay, to have it check every 300 seconds and run the command 'beep' (# apt install beep), run:
Code: Select all
$ mailnum -c beep
Sure, just set it to alert once with the -a parameter:
Code: Select all
$ mailnum -c -a beep
Code: Select all
$ mailnum -c -a 400 beep && procure grandma = tofu sandwhich { if (!grandmaWell) hold = sauce; }
But, bedtime, it keeps telling me that I have new email, but I don't; I've checked over 10 times to make sure!
Reset the count like so:
Code: Select all
$ mailnum -r
Is it possible that you are confusing this program with your grandmothers building?
mailnum.cpp:
// 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):
// $ mail -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;
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{ // 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];
}
}
const char * charNewMailCmd = newMailCmd.c_str(); // system() won't accept a string
std::chrono::milliseconds timespan(secsToNextCheck * 1000);
// 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, "yourUserName@yourEmail.com");
curl_easy_setopt(curl, CURLOPT_PASSWORD, "yourPasswordReplacesThisText");
curl_easy_setopt(curl, CURLOPT_URL, "imaps://mail.yourMailServer.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;
// You may need to check if the program is finding your home directory
// std::cout << fileName << std::endl;
// Open file with last email count to compare
ifstream testFile(fileName);
// Check if file exists or contains data
if (testFile.fail())
{
// Make .config if not there
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();
}else{
getline (testFile,lastCount);
testFile.close();
// Check if content is a number
if (lastCount.empty() || !std::all_of(lastCount.begin(), lastCount.end(), ::isdigit))
{
std::ofstream file(fileName);
file << trueCount;
file.close();
}
}
testFile.close();
// Open. Get lastCount. Close.
ifstream myFile(fileName);
getline (myFile,lastCount); // Store contents to variable 'lastCount'
myFile.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;
}
Be sure to check github (https://gist.github.com/bathtime/d69b30 ... 6c7aa9ed5b) for updates to the application.
Enjoy!