-
Notifications
You must be signed in to change notification settings - Fork 1
/
github.c
255 lines (229 loc) · 6.88 KB
/
github.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
#include "github.h"
#include <string.h>
#include <curl/curl.h>
/*
* Function: ensure_input
*
* asks the user to choose a number in range of zero and a given option amount
* repeats, if the input is invalid, until it's not
*
* options: the amount of options the user needs to choose from
* (must be a positive integer)
*
* returns: the valid option the user has chosen at last
* or -1 if the amount of options given is not a positive integer
*/
int ensure_input(int options)
{
char *line = NULL;
size_t n;
int res;
if (options<1) {
printf("Error: there are no options.\n");
return -1;
}
int input=-1, status;
while (input < 0 || input >= options) {
printf("Specify a target in range [0..%i]:\n", options-1);
res = getline(&line, &n, stdin);
if (line[res - 1] == '\n') {
line[res-1] = '\0';
}
status = sscanf(line, "%d", &input);
free(line);
line = NULL;
while (status != 1) {
printf("Invalid Input.\nSpecify a target in range [0..%i]:\n", options-1);
res = getline(&line, &n, stdin);
if (line[res - 1] == '\n') {
line[res-1] = '\0';
}
status = sscanf(line, "%d", &input);
free(line);
line = NULL;
}
}
return input;
}
/*
* Function: capped_amount_warning
*
* print a warning, if not all results are shown by find_users()
*
* arraylength: how many results are actually shown
*
* resultamount: how many results were found by a search
*/
void capped_amount_warning(int arraylength, int resultamount){
if(arraylength<resultamount) {
printf("Warning: The search produced %d results, but only %d are shown.\n",
resultamount, arraylength);
printf(" Please specify the search query to reduce the number.\n");
}
}
/*
* Function: fetch_jobj
*
* run a curl request against a json-based api and get the json_object
*
* url: a string containing the target url
*
* returns: the pointer to a json_object struct
*/
__attribute__((weak))
struct json_object* fetch_jobj(char *url)
{
CURL *curl;
CURLcode res;
struct MemoryStruct chunk;
chunk.memory = malloc(1);
chunk.size = 0;
struct json_object *jobj = NULL;
curl = curl_easy_init();
if(curl) {
curl_easy_setopt(curl, CURLOPT_URL, url);
curl_easy_setopt(curl, CURLOPT_USERAGENT, "Kraken/1.0");
curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
// set wmcallback as writefunction and chunk as target
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteMemoryCallback);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&chunk);
res = curl_easy_perform(curl);
if(res != CURLE_OK) {
fprintf(stderr, "Error: %s\n",
curl_easy_strerror(res));
} else {
jobj = json_tokener_parse(chunk.memory);
}
}
curl_easy_cleanup(curl);
free(chunk.memory);
return jobj;
}
/*
* Function: get_keys
*
* prints all public keys a given user has uploaded to GitHub, using GitHubs API
*
* username: a string holding a (hopefully) valid GitHub username
*
* returns: an integer, whether the function ran into memory issues
*/
int get_keys(const char *username)
{
char *key_url = "https://api.github.com/users/%s/keys";
int arraylen, jtype;
int new_len = strlen(username)+strlen(key_url)-2+1;
char *built_url;
struct json_object *jobj = NULL, *tuplejobj, *keyjobj;
built_url = malloc(new_len * sizeof(char));
snprintf(built_url, new_len, key_url, username);
if (!built_url) {
printf("Error, malloc failed; no memory available.\n");
return EXIT_FAILURE;
}
jobj = fetch_jobj(built_url);
jtype = json_object_get_type(jobj);
if (json_type_array == jtype) {
arraylen = json_object_array_length(jobj);
for (int i=0; i<arraylen; i++) {
tuplejobj = json_object_array_get_idx(jobj, i);
json_object_object_get_ex(tuplejobj, "key", &keyjobj);
printf("%s %s@github\n", json_object_get_string(keyjobj), username);
}
}else{
printf("Error: User was not found.\n");
}
json_object_put(jobj);
free(built_url);
return EXIT_SUCCESS;
}
/*
* Function: find_user
*
* searches for a GitHub username interactively and afterwords
* prints all public keys, the chosen account has uploaded to GitHub, using GitHubs API
*
* username: a string holding a partial or similar username to an existing accountname, which will be looked up
*
* returns: an integer, whether the functions ran into memory issues
*/
int find_user(char *name)
{
char *baseurl = "https://api.github.com/search/users?q=";
char *url;
char *escaped_name;
struct json_object *jobj = NULL, *userjobj, *usernamejobj, *keyjobj;
struct json_object *returnObj, *amountObj;
int arraylen, jtype, target, resultamount;
escaped_name = curl_escape(name, 0);
url = malloc(strlen(baseurl)+strlen(escaped_name)+1);
if (!url) {
printf("Error, malloc failed; no memory available.\n");
return EXIT_FAILURE;
}
strcpy(url, baseurl);
strcat(url, escaped_name);
curl_free(escaped_name);
jobj = fetch_jobj(url);
free(url);
jtype = json_object_get_type(jobj);
if (json_type_object != jtype) {
json_object_put(jobj);
return EXIT_FAILURE;
}
json_object_object_get_ex(jobj, "items", &returnObj);
json_object_object_get_ex(jobj, "total_count", &amountObj);
arraylen = json_object_array_length(returnObj);
resultamount = json_object_get_int(amountObj);
for (int i=0; i<arraylen; i++) {
userjobj = json_object_array_get_idx(returnObj, i);
json_object_object_get_ex(userjobj, "login", &usernamejobj);
printf("%i: %s\n", i, json_object_get_string(usernamejobj));
}
if (arraylen>0) {
capped_amount_warning(arraylen, resultamount);
target = ensure_input(arraylen);
json_object_object_get_ex(json_object_array_get_idx(
returnObj, target),
"login", &keyjobj);
get_keys(json_object_get_string(keyjobj));
} else {
printf("Info: could not find a user with a name similar to '%s'.\n", name);
}
json_object_put(jobj);
return EXIT_SUCCESS;
}
/*
* Function WriteMemoryCallback
*
* A callback function to stepwise increase the size of a memory struct by one, as needed,
* in order to store data of priorly unknown size in ram, primarily used for curl requests
*
* in fact, the function creates a whole new MemoryStruct each time called just in order to
* seem like slowly increasing over time
*
* contents: a pointer to where the data will lie after the function call
*
* size: the size of one member in the MemoryStruct
*
* nmemb: the number of members in the MemoryStruct
*
* userp: the void pointer where the data lies
*
* returns: the size of the data as size_t after inreasing it
*/
size_t WriteMemoryCallback(void *contents, size_t size, size_t nmemb, void *userp)
{
size_t realsize = size * nmemb;
struct MemoryStruct *mem = (struct MemoryStruct *)userp;
mem->memory = realloc(mem->memory, mem->size + realsize + 1);
if(mem->memory == NULL) {
/* out of memory! */
printf("Error: not enough memory (realloc returned NULL)\n");
return 0;
}
memcpy(&(mem->memory[mem->size]), contents, realsize);
mem->size += realsize;
mem->memory[mem->size] = 0;
return realsize;
}