// nasza-klasa.pl mutual friend finder // Written by Leszek Godlewski #include #include #include #include // friends list request flags #define FLF_UID (1 << 0) #define FLF_FIRST_NAME (1 << 1) #define FLF_LAST_NAME (1 << 2) #define FLF_AVATAR (1 << 3) #define FLF_FRIENDS_COUNT (1 << 4) #define FLF_LOCATION (1 << 5) // city #define FLF_MSGC_STATUS (1 << 6) // messaging centre status #define FLF_MSGC_STATUS_DESC (1 << 7) // messaging centre status description string #define FLF_UNKNOWN (1 << 8) // unknown flag #define FLF_ARTIFICIALITY (1 << 9) // whether or not the person in the account is a fictitious one or a real one typedef unsigned char bool_t; #define btrue 1 #define bfalse 0 CURL *easyhandle = NULL; char *response = NULL; int responseSize = 0; void printHelp(char *argv0) { printf("Compares the friend lists of 2 given users and returns mutual friends.\n" "\n" "Usage: %s \n" " username - your nasza-klasa.pl username/e-mail address\n" " password - your nasza-klasa.pl password\n" " UID1 - the UID of the first person\n" " UID2 - the UID of the second person\n", argv0); } size_t writeDocument(void *buf, size_t size, size_t nmemb, void *p) { int bytes = size * nmemb; char *target; if (!response) { responseSize = bytes + 1; response = malloc(responseSize); target = response; } else { response = realloc(response, responseSize + bytes); target = response + responseSize - 1; responseSize += bytes; } memcpy(target, buf, bytes); response[responseSize - 1] = 0; // ensure a trailing 0 return bytes; } struct curl_slist *headers = NULL; void initCurl(void) { curl_global_init(CURL_GLOBAL_WIN32); easyhandle = curl_easy_init(); curl_easy_setopt(easyhandle, CURLOPT_WRITEFUNCTION, writeDocument); curl_easy_setopt(easyhandle, CURLOPT_COOKIEFILE, "dummyfile"); curl_easy_setopt(easyhandle, CURLOPT_FOLLOWLOCATION, 1); #if 0 // debugging curl_easy_setopt(easyhandle, CURLOPT_VERBOSE, 1); #endif // the server requires a user agent headers = curl_slist_append(headers, "User-Agent: nasza-klasa.pl mutual friend finder"); curl_easy_setopt(easyhandle, CURLOPT_HTTPHEADER, headers); } bool_t login(char *l, char *p) { char postdata[1024], *el, *ep; int retval; el = curl_easy_escape(easyhandle, l, strlen(l)); ep = curl_easy_escape(easyhandle, p, strlen(p)); snprintf(postdata, sizeof(postdata), "login=%s&password=%s", el, ep); curl_free(el); curl_free(ep); curl_easy_setopt(easyhandle, CURLOPT_POSTFIELDS, postdata); curl_easy_setopt(easyhandle, CURLOPT_URL, "http://nasza-klasa.pl/login"); retval = curl_easy_perform(easyhandle); curl_easy_setopt(easyhandle, CURLOPT_POSTFIELDS, ""); if (strstr(response, "Nieprawid\305\202owa nazwa u\305\274ytkownika lub has\305\202o.")) retval = bfalse; else retval = (retval == 0); free(response); response = NULL; responseSize = 0; return retval; } #define CLIENT_VERSION 135 // version of the nasza-klasa.pl software we're identifying as #define MAX_NAME_LENGTH 100 // limit present in the nasza-klasa.pl website's source code typedef struct { int uid; char fname[MAX_NAME_LENGTH]; char lname[MAX_NAME_LENGTH]; bool_t mutual; } contact_t; contact_t *contacts = NULL; int numContacts = 0; bool_t getContactLists(char *u1, char *u2) { char url[1024]; char *r1 = NULL, *r2 = NULL; char *in, *out; int retval, i, j, n; snprintf(url, sizeof(url), "http://nasza-klasa.pl/friends_list/%s/%d/%d", u1, FLF_UID | FLF_FIRST_NAME | FLF_LAST_NAME, CLIENT_VERSION); curl_easy_setopt(easyhandle, CURLOPT_URL, url); retval = curl_easy_perform(easyhandle); if (retval != 0) { if (response) { free(response); response = NULL; } return bfalse; } r1 = response; response = NULL; responseSize = 0; // we don't need as much info from the other person, fetch the UIDs only snprintf(url, sizeof(url), "http://nasza-klasa.pl/friends_list/%s/%d/%d", u2, FLF_UID, CLIENT_VERSION); curl_easy_setopt(easyhandle, CURLOPT_URL, url); retval = curl_easy_perform(easyhandle); if (retval != 0) { if (response) { free(response); response = NULL; } if (r1) free(r1); return bfalse; } r2 = response; response = NULL; responseSize = 0; // parse the first contact list // get the friends count and allocate memory in = strstr(r1 + 3, "\"FRIENDS_COUNT\":"); if (!in) { if (response) { free(response); response = NULL; } if (r1) free(r1); if (r2) free(r2); return bfalse; } in += 16; out = url; // reuse the variable while (*in < '0' || *in > '9') in++; while (*in >= '0' && *in <= '9') *out++ = *in++; *out = 0; numContacts = atoi(url); contacts = malloc(numContacts * sizeof(*contacts)); memset(contacts, 0, numContacts * sizeof(*contacts)); // get the UIDs in = strstr(r1 + 3, "\"UID\":"); if (!in) { if (response) { free(response); response = NULL; } if (r1) free(r1); if (r2) free(r2); free(contacts); return bfalse; } in += 6; while (*in != '[') in++; for (i = 0; i < numContacts; i++) { out = url; while (*in < '0' || *in > '9') in++; while (*in >= '0' && *in <= '9') *out++ = *in++; *out = 0; contacts[i].uid = atoi(url); } // get the first names in = strstr(r1 + 3, "\"FIRST_NAME\":"); if (!in) { if (response) { free(response); response = NULL; } if (r1) free(r1); if (r2) free(r2); free(contacts); return bfalse; } in += 13; while (*in != '[') in++; for (i = 0; i < numContacts; i++) { out = url; while (*in != '\"') in++; in++; while (*in != '\"') *out++ = *in++; in++; *out = 0; strncpy(contacts[i].fname, url, sizeof(contacts->fname)); } // get the last names in = strstr(r1 + 3, "\"LAST_NAME\":"); if (!in) { if (response) { free(response); response = NULL; } if (r1) free(r1); if (r2) free(r2); free(contacts); return bfalse; } in += 12; while (*in != '[') in++; for (i = 0; i < numContacts; i++) { out = url; while (*in != '\"') in++; in++; while (*in != '\"') *out++ = *in++; in++; *out = 0; strncpy(contacts[i].lname, url, sizeof(contacts->lname)); } // parse the second contact list // get the friends count in = strstr(r2 + 3, "\"FRIENDS_COUNT\":"); if (!in) { if (response) { free(response); response = NULL; } if (r1) free(r1); if (r2) free(r2); return bfalse; } in += 16; out = url; // reuse the variable while (*in < '0' || *in > '9') in++; while (*in >= '0' && *in <= '9') *out++ = *in++; *out = 0; retval = atoi(url); // reuse the variable // get the UIDs in = strstr(r2 + 3, "\"UID\":"); if (!in) { if (response) { free(response); response = NULL; } if (r1) free(r1); if (r2) free(r2); free(contacts); return bfalse; } in += 6; while (*in != '[') in++; for (i = 0; i < retval; i++) { out = url; while (*in < '0' || *in > '9') in++; while (*in >= '0' && *in <= '9') *out++ = *in++; *out = 0; n = atoi(url); for (j = 0; j < numContacts; j++) { if (contacts[j].uid == n) { contacts[j].mutual = btrue; break; } } } free(r1); free(r2); return btrue; } void logout(void) { curl_easy_setopt(easyhandle, CURLOPT_URL, "http://nasza-klasa.pl/logout?l=1"); curl_easy_perform(easyhandle); } void cleanupCurl(void) { curl_slist_free_all(headers); curl_easy_cleanup(easyhandle); curl_global_cleanup(); } void printMutualFriends(void) { int i; for (i = 0; i < numContacts; i++) { if (!contacts[i].mutual) continue; printf("%s %s (http://nasza-klasa.pl/profile/%d)\n", contacts[i].fname, contacts[i].lname, contacts[i].uid); } } int main(int argc, char *argv[]) { printf("nasza-klasa.pl mutual friend finder\n" "Written by Leszek Godlewski\n" "This program is free software under the WTFPL license.\n" "\n" ); if (argc != 5) { printHelp(argv[0]); exit(0); } initCurl(); printf("Logging in...\n"); if (!login(argv[1], argv[2])) { printf("Logging in failed. Check your login details and internet connection.\n"); exit(0); } printf("Downloading and processing contact lists...\n"); if (!getContactLists(argv[3], argv[4])) { printf("Retrieving contact lists failed. Check your internet connection.\n"); exit(0); } printf("Logging out...\n"); logout(); cleanupCurl(); printf("Mutual friends are:\n"); printMutualFriends(); free(contacts); return 0; }