Решил я что использование сокетов — это единственный вариант, который позволит быстро получить список контактов и делать это примерно одинаково для большинства почтовых сервисов. Суть в том, чтобы эмулировать работу браузера. Я рассмотрю алгоритм получения списка контактов с gmail.com, но подобным образом это делается и в других почтовых сервисах.
Для начала понадобится http sniffer. Я воспользовался HTTP Analyzer и к сожалению Internet Explorer лучше всех подошёл (Chrome вообще не снифится, а Mozilla Firefox многое не снифит).
Контакты делятся на категории, поэтому мы будем доставать из категорий «Контакты» и «Другие контакты». Для получения списка контактов, надо отправить следующие запросы:
GET /mail/?auth=%0 HTTP/1.1
Host: mail.google.com
Cookie: SID=%1
«GET /mail/c/u/0/data/contactstore?ac=false&ccnt=true&cr=true&ct=true&eu=1&ev=true&f=g4&gids=26ae&gp=false&hl=ru&id=personal&max=250&nge=true&out=js&pbd=true&sf=display&st=0&type=4 HTTP/1.1
Host: mail.google.com
Cookie: GX=%0
Поэтому потребуется достать значение параметра auth и куки SID и GX.
Приступим.
Входим в систему и сохраняем параметр sidt для accounts.youtube.com и кук SID:
QString GmailCom::postLogin() { int searchResult = -1; QString buffer = ""; QString temp = ""; QString endString = "\r\n"; QString endHead = "\r\n\r\n"; QString startSidt = "sidt="; QString startCookieSid = "SID="; QString autoLogin = "X-Auto-Login:"; QSslSocket socket; socket.connectToHostEncrypted("accounts.google.com", 443); if(!socket.waitForEncrypted(MAX_WAIT)) return "Error 0"; socket.write(QString("POST /ServiceLoginAuth HTTP/1.1\r\n"\ "Content-Type: application/x-www-form-urlencoded\r\n"\ "Host: accounts.google.com\r\n"\ "Content-Length: 741\r\n"\ "Cookie: GALX=P08JFYX33nQ;\r\n"\ "\r\n"\ "continue=http%3A%2F%2Fmail.google.com%2Fmail%2F&service=mail&rm=false&dsh=-8197000947972966366<mpl=default&scc=1&GALX=P08JFYX33nQ&pstMsg=1&dnConn=&checkConnection=youtube%3A1281%3A0&checkedDomains=youtube&timeStmp=&secTok=&_utf8=%E2%98%83&bgresponse=%21A0KXP079cDPUuUQp6r4pV6akFgIAAAFIUgAAAF4qARD5AbnvQQIZUPsKgyJAteOUThq8meti42P_lSOgXFYCB7HooRpk6np4p5WpvswQrM2ejpn0MjFQxlTHTjWuoyZeWUIsb9xxFDpxCCClPHPv2S3AB416Sq7sUB2IVzG2muVwYSLZ7I_EE99s2k10uDsq9l8nq7IV4QAxMMBRs_SUH-JsiFDBhg0c2xMGGjXvBtoJSGKXuY1d9wTKRx3AQ7hZTyP3vEnLoSX-bj8DruGDteXN1QSMsbLUVd2QQpCSdT6LyDVcEnqo2vJmfxLOvpb898yVrVILjYXona137oYoGy4Lc45CH9c4iicTXIaV4viayj5JlyjgBKPVr7gcYdQVEpLlU6sUGgVuU-RZ64rNLg&Email=%0&Passwd=%1&signIn=%D0%92%D0%BE%D0%B9%D1%82%D0%B8&rmShown=1").arg(mLogin).arg(mPassword).toUtf8()); while(socket.waitForReadyRead(MAX_WAIT)) { temp = socket.readAll(); buffer += temp; if(temp.indexOf(endHead) != -1) break; } socket.disconnectFromHost(); searchResult = buffer.indexOf(autoLogin); if(searchResult != -1) return "Error 1"; searchResult = buffer.indexOf(startCookieSid); if(searchResult == -1) return "Error 2"; buffer = buffer.mid(searchResult + startCookieSid.length()); mCookieSid = buffer.mid(0, buffer.indexOf(endString)); searchResult = buffer.indexOf(startSidt); if(searchResult == -1) return "Error 3"; buffer = buffer.mid(searchResult + startSidt.length()); mAccountsYoutubeSidt = buffer.mid(0, buffer.indexOf(endString)); return "Ok"; }
Затем получаем значение sidt для accounts.google.com:
QString GmailCom::getAccountsGoogle() { int searchResult = -1; QString buffer = ""; QString temp = ""; QString endString = "\r\n"; QString endHead = "\r\n\r\n"; QString startSidt = "sidt="; QSslSocket socket; socket.connectToHostEncrypted("accounts.youtube.com", 443); if(!socket.waitForEncrypted(MAX_WAIT)) return "Error 4"; socket.write(QString("GET /accounts/SetSID?ssdc=1&sidt=%0 HTTP/1.1\r\n"\ "Host: accounts.youtube.com\r\n"\ "\r\n").arg(mAccountsYoutubeSidt).toUtf8()); while(socket.waitForReadyRead(MAX_WAIT)) { temp = socket.readAll(); buffer += temp; if(temp.indexOf(endHead) != -1) break; } socket.disconnectFromHost(); searchResult = buffer.indexOf(startSidt); if(searchResult == -1) return "Error 5"; buffer = buffer.mid(searchResult + startSidt.length()); mAccountsGoogleSidt = buffer.mid(0, buffer.indexOf(endString)); return "Ok"; }
После чего получаем значение auth для mail.google.com:
QString GmailCom::getAccountsMail() { int searchResult = -1; QString buffer = ""; QString temp = ""; QString endString = "\r\n"; QString endHead = "\r\n\r\n"; QString startAuth = "auth="; QSslSocket socket; socket.connectToHostEncrypted("accounts.youtube.com", 443); if(!socket.waitForEncrypted(MAX_WAIT)) return "Error 6"; socket.write(QString("GET /accounts/SetSID?ssdc=1&sidt=%0 HTTP/1.1\r\n"\ "Host: accounts.google.md\r\n"\ "\r\n").arg(mAccountsGoogleSidt).toUtf8()); while(socket.waitForReadyRead(MAX_WAIT)) { temp = socket.readAll(); buffer += temp; if(temp.indexOf(endHead) != -1) break; } socket.disconnectFromHost(); searchResult = buffer.indexOf(startAuth); if(searchResult == -1) return "Error 7"; buffer = buffer.mid(searchResult + startAuth.length()); mMailGoogleAuth = buffer.mid(0, buffer.indexOf(endString)); return "Ok"; }
И достаём наш искомый кук GX:
QString GmailCom::getMailAuth() { int searchResult = -1; QString buffer = ""; QString temp = ""; QString endString = "\r\n"; QString endHead = "\r\n\r\n"; QString startGx = "GX="; QSslSocket socket; socket.connectToHostEncrypted("mail.google.com", 443); if(!socket.waitForEncrypted(MAX_WAIT)) return "Error 8"; socket.write(QString("GET /mail/?auth=%0 HTTP/1.1\r\n"\ "Host: mail.google.com\r\n"\ "Cookie: SID=%1\r\n"\ "\r\n").arg(mMailGoogleAuth).arg(mCookieSid).toUtf8()); while(socket.waitForReadyRead(MAX_WAIT)) { temp = socket.readAll(); buffer += temp; if(temp.indexOf(endHead) != -1) break; } socket.disconnectFromHost(); searchResult = buffer.indexOf(startGx); if(searchResult == -1) return "Error 9"; buffer = buffer.mid(searchResult + startGx.length()); mGxCookie = buffer.mid(0, buffer.indexOf(endString)); return "Ok"; }
Теперь можно получить список контактов:
QString GmailCom::getMyContacts() { int searchResult = -1; QStringList listContacts; QString stringContacts; QString buffer = ""; QString startContacts = "&&&START&&&"; QString endContacts = "&&&END&&&"; QString temp = ""; QString rn = "\r\n"; QSslSocket socket; socket.connectToHostEncrypted("mail.google.com", 443); if(!socket.waitForEncrypted(MAX_WAIT)) return "Error 10"; socket.write(QString("GET /mail/c/u/0/data/contactstore?ac=false&ccnt=true&cr=true&ct=true&eu=1&ev=true&f=g4&gids=6&gp=false&hl=ru&id=personal&max=250&nge=true&out=js&pbd=true&type=4 HTTP/1.1\r\n"\ "Host: mail.google.com\r\n"\ "Cookie: GX=%0\r\n"\ "\r\n").arg(mGxCookie).toUtf8()); while(socket.waitForReadyRead(MAX_WAIT)) { temp = socket.readAll(); buffer += temp; if(temp.indexOf(endContacts) != -1) break; } socket.disconnectFromHost(); searchResult = buffer.indexOf(startContacts); if(searchResult == -1) return "Error 11"; buffer = buffer.mid(searchResult + startContacts.length()); searchResult = buffer.indexOf(endContacts); if(searchResult == -1) return "Error 11"; buffer = buffer.mid(0, searchResult); while(1) { int rn_1 = buffer.indexOf(rn, 0); int rn_2 = buffer.indexOf(rn, rn_1 + rn.length()); if(rn_1 == -1 || rn_2 == -1) break; else buffer.remove(rn_1, rn_2 - rn_1 + rn.length()); } listContacts = buffer.split("\\\""); listContacts.removeDuplicates(); for(int i = 0; i < listContacts.size(); i++) if(listContacts[i].indexOf("@") != -1) stringContacts += listContacts[i] + " "; return stringContacts; }
И список контактов из группы «Другие контакты»:
QString GmailCom::getGroupContacts() { int searchResult = -1; QStringList listContacts; QString stringContacts; QString buffer = ""; QString startContacts = "&&&START&&&"; QString endContacts = "&&&END&&&"; QString temp = ""; QString rn = "\r\n"; QSslSocket socket; socket.connectToHostEncrypted("mail.google.com", 443); if(!socket.waitForEncrypted(MAX_WAIT)) return "Error 12"; socket.write(QString("GET /mail/c/u/0/data/contactstore?ac=false&ccnt=true&cr=true&ct=true&eu=1&ev=true&f=g4&gids=26ae&gp=false&hl=ru&id=personal&max=250&nge=true&out=js&pbd=true&sf=display&st=0&type=4 HTTP/1.1\r\n"\ "Host: mail.google.com\r\n"\ "Cookie: GX=%0\r\n"\ "\r\n" ).arg(mGxCookie).toUtf8()); while(socket.waitForReadyRead(MAX_WAIT)) { temp = socket.readAll(); buffer += temp; if(temp.indexOf(endContacts) != -1) break; } socket.disconnectFromHost(); searchResult = buffer.indexOf(startContacts); if(searchResult == -1) return "Error 13"; buffer = buffer.mid(searchResult + startContacts.length()); searchResult = buffer.indexOf(endContacts); if(searchResult == -1) return "Error 13"; buffer = buffer.mid(0, searchResult); while(1) { int rn_1 = buffer.indexOf(rn, 0); int rn_2 = buffer.indexOf(rn, rn_1 + rn.length()); if(rn_1 == -1 || rn_2 == -1) break; else buffer.remove(rn_1, rn_2 - rn_1 + rn.length()); } listContacts = buffer.split("\\\""); listContacts.removeDuplicates(); for(int i = 0; i < listContacts.size(); i++) if(listContacts[i].indexOf("@") != -1) stringContacts += listContacts[i] + " "; return stringContacts; }
Ещё есть конструктор, который инициализирует логин и пароль при создании объекта:
GmailCom::GmailCom(QString login, QString password): mLogin(login), mPassword(password) {}
И сам метод получения списка контактов:
QString GmailCom::getContacts() { QString temp; QString error; if((error = postLogin()) != "Ok") return error; if((error = getAccountsGoogle()) != "Ok") return error; if((error = getAccountsMail()) != "Ok") return error; if((error = getMailAuth()) != "Ok") return error; QString stringContacts = ""; temp = getMyContacts(); if(temp.split(' ')[0] == "Error") return temp; stringContacts += temp; temp = getGroupContacts(); if(temp.split(' ')[0] == "Error") return temp; stringContacts += temp; stringContacts.remove(stringContacts.length() - 1, 1); return stringContacts; }
Хедер выглядит следующим образом:
#ifndef GMAILCOM_H #define GMAILCOM_H #include <QSslSocket> #include <QNetworkProxy> #include <QStringList> #define MAX_WAIT 3000 class GmailCom { private: QString mLogin; QString mPassword; QString mAccountsYoutubeSidt; QString mAccountsGoogleSidt; QString mMailGoogleAuth; QString mCookieSid; QString mGxCookie; QString postLogin(); QString getAccountsGoogle(); QString getAccountsMail(); QString getMailAuth(); QString getMyContacts(); QString getGroupContacts(); public: explicit GmailCom(QString login, QString password); explicit GmailCom(QString login, QString password, QString proxyHostName, int proxyPort); explicit GmailCom(QString login, QString password, QString proxyHostName, int proxyPort, QString proxyUser, QString proxyPassword); QString getContacts(); }; #endif
Использование класса:
#include <QCoreApplication> #include "GmailCom.h" int main(int argc, char *argv[]) { QCoreApplication a(argc, argv); GmailCom gmailCom("login", "password"); qDebug() << gmailCom.getContacts(); return a.exec(); }
И наконец *.pro файл:
#------------------------------------------------- # # Project created by QtCreator 2013-10-23T09:40:42 # #------------------------------------------------- QT += core QT += network QT -= gui TARGET = Mailer CONFIG += console CONFIG -= app_bundle TEMPLATE = app SOURCES += main.cpp \ GmailCom.cpp HEADERS += \ GmailCom.h
Всё общение с сервером происходит по защищённому соединению. Код полностью рабочий. Если скопипастить поочерёдно каждый блок кода и поместить в соответствующие файлы — он будет компилироваться. Использовал компилятор g++, среда разработки Qt creator, версия 5.1
ссылка на оригинал статьи http://habrahabr.ru/post/203034/
Добавить комментарий