Add a bunch of overflow checks near malloc()s
[nci.git] / nci-get-assignment-grades.c
blobd4b2850d267cd0600fdb36b5b18788a5becbac33
1 /*
2 * Copyright (c) 2016, S. Gilles <sgilles@math.umd.edu>
4 * Permission to use, copy, modify, and/or distribute this software
5 * for any purpose with or without fee is hereby granted, provided
6 * that the above copyright notice and this permission notice appear
7 * in all copies.
9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
10 * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
11 * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
12 * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR
13 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
14 * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
15 * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
16 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18 #include <errno.h>
19 #include <locale.h>
20 #include <stdint.h>
21 #include <stdint.h>
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <strings.h>
25 #include <unistd.h>
27 #include <curl/curl.h>
28 #include <yajl_parse.h>
30 #include "macros.h"
31 #include "util.h"
33 /* Write output to stdout */
34 static void write_out_everything(char *course_id, map *students, map **grades,
35 map *totals, size_t assignments_num,
36 char **assignment_ids, map *assignments)
38 size_t j;
39 size_t k;
40 char **student_names = 0;
41 char *name;
42 char **id;
43 char **score;
44 char **res;
45 size_t students_num;
47 map_get_keys(students, &student_names, &students_num);
48 printf("\"Name\",\"ID\"");
50 for (j = 0; j < assignments_num; ++j) {
51 printf(",\"%s:%s (", course_id, assignment_ids[j]);
52 res = map_get(assignments, assignment_ids[j]);
54 if (res &&
55 res[0]) {
56 print_esc_0x22(res[0]);
59 printf(")\"");
62 if (totals) {
63 printf(",\"Total\",\"Letter Grade\"");
66 printf("\n");
68 for (j = 0; j < students_num; ++j) {
69 name = student_names[j];
70 id = map_get(students, name);
72 if (!id ||
73 !id[0]) {
74 continue;
77 printf("\"");
78 print_esc_0x22(name);
79 printf("\",\"%s\"", id[0]);
81 for (k = 0; k < assignments_num; ++k) {
82 score = map_get(&((*grades)[k]), id[0]);
83 printf(",\"");
85 if (score &&
86 score[1] &&
87 !strcasecmp(score[1], "true")) {
88 print_esc_0x22("EX");
89 } else if (score &&
90 score[0]) {
91 print_esc_0x22(score[0]);
94 printf("\"");
97 if (totals) {
98 score = map_get(totals, id[0]);
99 printf(",\"");
101 if (score &&
102 score[0]) {
103 print_esc_0x22(score[0]);
106 printf(",\"");
108 if (score &&
109 score[1]) {
110 print_esc_0x22(score[1]);
113 printf("\"");
116 printf("\n");
119 free(student_names);
122 int main(int argc, char **argv)
124 int ret = EINVAL;
125 char *url_base = 0;
126 char *auth_token = 0;
127 char *course_id = 0;
128 const char *fn_students[] = { "sortable_name", "id", 0 };
129 const char *fn_scores[] = { "user_id", "score", "excused", 0 };
130 const char *fn_totals[] = { "user_id", "final_score", "final_grade",
131 0 };
132 const char *fn_assignments[] = { "id", "name", 0 };
133 uint_fast8_t fetch_all_arg = 0;
134 uint_fast8_t totals_arg = 0;
135 char **assignment_ids = 0;
136 map students = { 0 };
137 map *grades = 0;
138 map totals = { 0 };
139 map assignments = { 0 };
140 size_t j = 0;
141 size_t assignments_num = 0;
142 size_t len = 0;
143 char *built_uri = 0;
144 int opt = 0;
146 setlocale(LC_ALL, "");
148 if (!(assignment_ids = calloc((argc / 2), sizeof *assignment_ids))) {
149 ret = errno;
150 perror(L("calloc"));
151 goto cleanup;
154 while ((opt = getopt(argc, argv, "c:a:At")) != -1) {
155 switch (opt) {
156 case 'c':
157 course_id = optarg;
158 break;
159 case 'a':
160 assignment_ids[assignments_num++] = optarg;
161 break;
162 case 'A':
163 fetch_all_arg = 1;
164 break;
165 case 't':
166 totals_arg = 1;
167 break;
168 default:
169 break;
173 if (fetch_all_arg &&
174 assignments_num) {
175 fprintf(stderr, "both -A and -a provided, ignoring -a\n");
178 if (!course_id) {
179 ret = EINVAL;
180 fprintf(stderr, "course-id is mandatory\n");
181 goto cleanup;
184 if (!fetch_all_arg &&
185 !assignments_num &&
186 !totals_arg) {
187 ret = EINVAL;
188 fprintf(stderr, "one of -a, -A, -t is mandatory\n");
189 goto cleanup;
192 if (!(grades = calloc(assignments_num, sizeof *grades))) {
193 ret = errno;
194 perror(L("calloc"));
195 goto cleanup;
198 curl_global_init(CURL_GLOBAL_DEFAULT);
200 if (!(url_base = get_url_base())) {
201 /* Error should have already been printed */
202 ret = ENOENT;
203 goto cleanup;
206 if (!(auth_token = get_auth_token())) {
207 /* Error should have already been printed */
208 ret = ENOENT;
209 goto cleanup;
212 /* Get a list of all students, so we can find out their names */
213 len = snprintf(0, 0, "%s/api/v1/courses/%s/students?per_page=9999",
214 url_base, course_id);
216 if (len + 1 < len) {
217 ret = errno = EOVERFLOW;
218 perror(L(""));
219 goto cleanup;
222 if (!(built_uri = malloc(len + 1))) {
223 ret = errno;
224 perror(L("malloc"));
225 goto cleanup;
228 sprintf(built_uri, "%s/api/v1/courses/%s/students?per_page=9999",
229 url_base, course_id);
231 if ((ret = key_value_extract(built_uri, auth_token, fn_students, 0,
232 &students))) {
233 goto cleanup;
236 /* Get a list of all assignments, so we can find out their names */
237 free(built_uri);
238 len = snprintf(0, 0, "%s/api/v1/courses/%s/assignments?per_page=9999",
239 url_base, course_id);
241 if (len + 1 < len) {
242 ret = errno = EOVERFLOW;
243 perror(L(""));
244 goto cleanup;
247 if (!(built_uri = malloc(len + 1))) {
248 ret = errno;
249 perror(L("malloc"));
250 goto cleanup;
253 sprintf(built_uri, "%s/api/v1/courses/%s/assignments?per_page=9999",
254 url_base, course_id);
256 if ((ret = key_value_extract(built_uri, auth_token, fn_assignments, 0,
257 &assignments))) {
258 goto cleanup;
261 if (fetch_all_arg) {
262 assignments_num = 0;
263 free(assignment_ids);
264 assignment_ids = 0;
265 free(grades);
266 grades = 0;
267 map_get_keys(&assignments, &assignment_ids, &assignments_num);
269 if (!assignments_num) {
270 ret = ENOMEM;
271 perror(L("map_get_keys"));
272 goto cleanup;
275 if (!(grades = calloc(assignments_num, sizeof *grades))) {
276 ret = errno;
277 perror(L("calloc"));
278 goto cleanup;
282 for (j = 0; j < assignments_num; ++j) {
283 free(built_uri);
284 len = snprintf(0, 0, "%s/api/v1/courses/%s/assignments/%s/"
285 "submissions?per_page=9999", url_base,
286 course_id,
287 assignment_ids[j]);
289 if (len + 1 < len) {
290 ret = errno = EOVERFLOW;
291 perror(L(""));
292 goto cleanup;
295 if (!(built_uri = malloc(len + 1))) {
296 ret = errno;
297 perror(L("malloc"));
298 goto cleanup;
301 sprintf(built_uri, "%s/api/v1/courses/%s/assignments/%s/"
302 "submissions?per_page=9999", url_base,
303 course_id,
304 assignment_ids[j]);
306 if ((ret = key_value_extract(built_uri, auth_token, fn_scores,
307 0, &(grades[j])))) {
308 goto cleanup;
312 if (totals_arg) {
313 free(built_uri);
314 len = snprintf(0, 0,
315 "%s/api/v1/courses/%s/enrollments?per_page=9999",
316 url_base,
317 course_id);
319 if (len + 1 < len) {
320 ret = errno = EOVERFLOW;
321 perror(L(""));
322 goto cleanup;
325 if (!(built_uri = malloc(len + 1))) {
326 ret = errno;
327 perror(L("malloc"));
328 goto cleanup;
331 sprintf(built_uri,
332 "%s/api/v1/courses/%s/enrollments?per_page=9999",
333 url_base,
334 course_id);
336 if ((ret = key_value_extract(built_uri, auth_token, fn_totals,
337 0, &totals))) {
338 goto cleanup;
342 write_out_everything(course_id, &students, &grades, (totals_arg ?
343 &totals : 0),
344 assignments_num, assignment_ids, &assignments);
345 ret = 0;
346 cleanup:
347 free(built_uri);
348 free(url_base);
349 free(auth_token);
350 curl_global_cleanup();
351 free(assignment_ids);
352 map_clean(&students);
353 map_clean(&assignments);
354 map_clean(&totals);
356 if (assignments_num) {
357 do {
358 map_clean(&grades[--assignments_num]);
359 } while (assignments_num);
362 free(grades);
364 return ret;