GCC Code Coverage Report


src/
File: src/api/internal.c
Date: 2026-03-31 17:48:22
Lines:
36/86
41.9%
Functions:
4/10
40.0%
Branches:
20/65
30.8%

Line Branch Exec Source
1 #include <glib.h>
2 #include <stdio.h>
3 #include <string.h>
4 #include <time.h>
5 #include "api/internal.h"
6 #include "config/config.h"
7
8 static const char DEFAULT_API_URL[] = "https://gittor.rent/api";
9 static const char USER_AGENT[] = "GitTor-CLI/dev"; // Hardcoded for now
10
11 extern response_buf_t response_buf_init() {
12 response_buf_t buf;
13 buf.data = g_malloc(1);
14 buf.data[0] = '\0';
15 buf.size = 0;
16 return buf;
17 }
18
19 extern size_t write_cb(void* ptr, size_t size, size_t nmemb, void* userdata) {
20 size_t total = size * nmemb;
21 response_buf_t* buf = (response_buf_t*)userdata;
22
23 // Reallocate buffer for new data + null terminator
24 char* tmp = g_realloc(buf->data, buf->size + total + 1);
25 if (!tmp)
26 return 0;
27
28 // Append new data
29 buf->data = tmp;
30 memcpy(buf->data + buf->size, ptr, total); // NOLINT - safe since realloc
31 buf->size += total;
32 buf->data[buf->size] = '\0';
33
34 return total;
35 }
36
37 extern size_t write_file_cb(void* ptr,
38 size_t size,
39 size_t nmemb,
40 void* stream) {
41 return fwrite(ptr, size, nmemb, (FILE*)stream);
42 }
43
44 2 extern char* api_get_base_url() {
45 // Get the API URL from config
46 char* endpoint_url =
47 4 config_get(CONFIG_SCOPE_LOCAL,
48
1/1
✓ Branch 1 taken 2 times.
2 &(config_id_t){.group = "network", .key = "api_url"}, NULL);
49
50 // If not set, use the default
51
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
2 if (!endpoint_url) {
52 1 endpoint_url = g_strdup(DEFAULT_API_URL);
53
1/1
✓ Branch 1 taken 1 times.
1 g_print("Network API URL not set in config, using default: %s\n",
54 DEFAULT_API_URL);
55 }
56
57 // Remove trailing slashes
58 2 size_t len = strlen(endpoint_url);
59
3/4
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 2 times.
3 while (len > 0 && endpoint_url[len - 1] == '/') {
60 1 endpoint_url[--len] = '\0';
61 }
62
63 2 return endpoint_url;
64 }
65
66 extern char* api_get_token() {
67 return config_get(CONFIG_SCOPE_LOCAL,
68 &(config_id_t){.group = "auth", .key = "access_token"},
69 NULL);
70 }
71
72 extern struct curl_slist* api_auth_headers(struct curl_slist* headers) {
73 char* token = api_get_token();
74 if (token && token[0]) {
75 char* hdr = g_strdup_printf("Authorization: Bearer %s", token);
76 headers = curl_slist_append(headers, hdr);
77 g_free(hdr);
78 }
79
80 g_free(token);
81 return headers;
82 }
83
84 2 extern CURL* api_curl_handle_new() {
85 2 CURL* curl = curl_easy_init();
86
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (!curl)
87 return NULL;
88
89 2 curl_easy_setopt(curl, CURLOPT_TIMEOUT, 30L); // 30 second timeout
90 2 curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 10L);
91 2 curl_easy_setopt(curl, CURLOPT_USERAGENT, USER_AGENT);
92 2 curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
93
94 2 return curl;
95 }
96
97 2 extern int api_build_url(char* out,
98 size_t out_size,
99 const char* path_fmt,
100 ...) {
101 // Get the base URL
102
1/1
✓ Branch 1 taken 2 times.
2 char* base_url = api_get_base_url();
103
104
1/1
✓ Branch 1 taken 2 times.
2 int base_len = g_snprintf(out, out_size, "%s", base_url);
105
1/1
✓ Branch 1 taken 2 times.
2 g_free(base_url);
106
107
2/4
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 2 times.
2 if (base_len < 0 || (size_t)base_len >= out_size)
108 2 return -1;
109
110 // Append the path
111 va_list args;
112 2 va_start(args, path_fmt);
113 int path_len =
114
1/1
✓ Branch 1 taken 2 times.
2 g_vsnprintf(out + base_len, out_size - base_len, path_fmt, args);
115 2 va_end(args);
116
117
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 return (path_len < 0 || (size_t)(base_len + (size_t)path_len) >= out_size)
118 ? -1
119
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
4 : 0;
120 }
121
122 2 extern api_result_e api_check_response(CURL* curl, CURLcode res) {
123
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (res != CURLE_OK)
124 2 return API_CURL_ERR;
125
126 2 long response_code = 0; // NOLINT(runtime/int) - required by curl API
127
1/1
✓ Branch 1 taken 2 times.
2 curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &response_code);
128
129
2/4
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
2 if (response_code >= 200 && response_code < 300)
130 2 return API_OK;
131
132 if (response_code == 400)
133 return API_BAD_REQUEST;
134 if (response_code == 401 || response_code == 403)
135 return API_FORBIDDEN;
136 if (response_code == 404)
137 return API_NOT_FOUND;
138
139 if (response_code >= 500 && response_code < 600)
140 return API_SERVER_ERR;
141
142 printf("Unexpected response code: %ld\n", response_code);
143 return API_CURL_ERR; // Treat other codes as errors
144 }
145
146 extern int parse_expiry_epoch(const char* expires, time_t* epoch_out) {
147 if (!expires || expires[0] == '\0' || !epoch_out) {
148 return EINVAL;
149 }
150
151 errno = 0;
152 char* endptr = NULL;
153 gint64 value = g_ascii_strtoll(expires, &endptr, 10);
154 if (errno != 0 || endptr == expires || *endptr != '\0' || value < 0)
155 return EINVAL;
156
157 *epoch_out = (time_t)value;
158 return 0;
159 }
160