Check your email minimally and run command on new email

Share your own howto's etc. Not for support questions!

Check your email minimally and run command on new email

Postby bedtime » 2018-03-04 23:37

First, we need to make sure that you are able to run this application, so please make sure check the the following requirements:

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). :P


Introducing...

...wait for it...

...wait for it...

...waaaiiiiiiiitttttt...

... ... ...

:D !! M A I L N U M !! :D

Okay, the name isn't the most exciting. :roll: ... 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");


Then compile with:
Code: Select all
$ g++ -O -Wall mailnum.cpp -o mailnum -L/usr/include/curl/lib -lcurl -std=gnu++14


If you don't have the g++ software:
Code: Select all
$ sudo apt install build-essential


And yes, I understand that using a password in this file provides less security than a common life insurance scam (#EDIT# plan)... :roll:


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


If there is new mail, you would see:
Code: Select all
[1 new email]


But, bedtime, I'm too lazy to keep checking like that. :( Is there a way to have it keep checking automatically?

Yes, to have it check every 300 seconds (default), run:
Code: Select all
$ mailnum -c


But, bedtime, could you have it run a command when a new mail arrives? I don't want to have to keep looking at the number to see. :(

Okay, to have it check every 300 seconds and run the command 'beep' (# apt install beep), run:
Code: Select all
$ mailnum -c beep


I don't want the beeping to keep going off every 300 seconds. I only want it to go off once when a new mail arrives. :(

Sure, just set it to alert once with the -a parameter:
Code: Select all
$ mailnum -c -a beep


But, I actually want it to off every 400 seconds (there is a difference, you know :roll:), and I live in my grandmothers basement and sometimes she emails me when she wants me to make her a tofu sandwich, so could you give me the command to do that? I appreciate it. :(
Code: Select all
$ mailnum -c -a 400 beep && procure grandma = tofu sandwhich { if (!grandmaWell) hold = sauce; }


As you can see above, there is no need to use quotes, unless you are using commands with tricky regular expressions or with numbers; the program will figure it out for you.

But, bedtime, it keeps telling me that I have new email, but I don't; I've checked over 10 times to make sure! :shock:

Reset the count like so:
Code: Select all
$ mailnum -r


But, your program has more bugs than a Detroit apartment building. :(

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/d69b307094ce65c503cc4e6c7aa9ed5b) for updates to the application.

Enjoy! :)
bedtime
 
Posts: 55
Joined: 2012-12-16 19:34

Return to Docs, Howtos, Tips & Tricks

Who is online

Users browsing this forum: No registered users and 2 guests

fashionable