1 #include <string.h>
2 #include <stdbool.h>
3 #include <stdlib.h>
4 #include <ctype.h>
5
6 #include <glib.h>
7 #include <gdk-pixbuf/gdk-pixbuf.h>
8
9 #ifdef _WIN32
10 # include <windows.h>
11 #endif
12
13 #include <curl/curl.h>
14
15 #include "memfile.h"
16 #include "from_url.h"
17
18 #define REQUEST_TIMEOUT (5)
19
20 CURLcode
memfile_from_url(const memfile_from_url_info info)21 memfile_from_url(const memfile_from_url_info info) {
22 CURL* curl = curl_easy_init();
23 if (!curl) return CURLE_FAILED_INIT;
24
25 MEMFILE* body = memfopen();
26 long code = 0;
27 double csize = -1;
28 char* ctype = NULL;
29
30 curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0);
31 curl_easy_setopt(curl, CURLOPT_URL, info.url);
32 curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, REQUEST_TIMEOUT);
33 curl_easy_setopt(curl, CURLOPT_TIMEOUT, REQUEST_TIMEOUT);
34 curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, info.body_writer);
35 curl_easy_setopt(curl, CURLOPT_WRITEDATA, body);
36 curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1);
37 const CURLcode res = curl_easy_perform(curl);
38
39 if (res == CURLE_OK) {
40 curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &code);
41
42 if (curl_easy_getinfo(curl, CURLINFO_CONTENT_LENGTH_DOWNLOAD, &csize) != CURLE_OK)
43 csize = -1;
44 curl_easy_getinfo(curl, CURLINFO_CONTENT_TYPE, &ctype);
45 }
46
47 if (info.code) *info.code = code;
48 if (info.csize) *info.csize = csize;
49 if (info.ctype) *info.ctype = ctype ? strdup(ctype) : NULL;
50 if (info.body) *info.body = memfrelease(&body);
51 memfclose(body);
52
53 curl_easy_cleanup(curl);
54
55 return res;
56 }
57
58 // Some error happened only if returns true.
59 static bool
gerror_set_or_free(GError ** dest,GError * val)60 gerror_set_or_free(GError** dest, GError* val) {
61 if (!val) return false;
62
63 if (dest) *dest = val;
64 else g_error_free(val);
65 return true;
66 }
67
68 // FIXME: More refactor this function.
69 static GdkPixbuf*
pixbuf_from_url_impl(const char * ctype,const MEMFILE * raw,GError ** error)70 pixbuf_from_url_impl(const char* ctype, const MEMFILE* raw, GError** error) {
71 GdkPixbuf* pixbuf = NULL;
72 #ifdef _WIN32
73 if (ctype && (!strcmp(ctype, "image/jpeg") || !strcmp(ctype, "image/gif"))) {
74 char temp_path[MAX_PATH];
75 char temp_filename[MAX_PATH];
76 GetTempPath(sizeof(temp_path), temp_path);
77 GetTempFileName(temp_path, "growl-for-linux-", 0, temp_filename);
78 FILE* const fp = fopen(temp_filename, "wb");
79 if (fp) {
80 fwrite(memfdata(raw), memfsize(raw), 1, fp);
81 fclose(fp);
82 }
83 pixbuf = gdk_pixbuf_new_from_file(temp_filename, NULL);
84 DeleteFile(temp_filename);
85 } else
86 #endif
87 {
88 GError* _error = NULL;
89 GdkPixbufLoader* const loader =
90 ctype ? gdk_pixbuf_loader_new_with_mime_type(ctype, &_error)
91 : gdk_pixbuf_loader_new();
92 if (!gerror_set_or_free(error, _error)) {
93 if (gdk_pixbuf_loader_write(loader, (const guchar*) memfcdata(raw), memfsize(raw), &_error))
94 pixbuf = gdk_pixbuf_loader_get_pixbuf(loader);
95 else
96 gerror_set_or_free(error, _error);
97
98 gdk_pixbuf_loader_close(loader, NULL);
99 }
100 }
101 return pixbuf;
102 }
103
104 GdkPixbuf*
pixbuf_from_url(const char * url,GError ** error)105 pixbuf_from_url(const char* url, GError** error) {
106 if (!url) return NULL;
107 if (!strncmp(url, "x-growl-resource://", 19)) {
108 const gchar* const confdir = (const gchar*) g_get_user_config_dir();
109 gchar* const resourcedir = g_build_path(G_DIR_SEPARATOR_S, confdir, "gol", "resource", NULL);
110 const gchar* const newurl = g_build_filename(resourcedir, url + 19, NULL);
111 GdkPixbuf* pixbuf = pixbuf_from_url_as_file(newurl, error);
112 g_free(resourcedir);
113 return pixbuf;
114 }
115
116 MEMFILE* mbody;
117 long code;
118 double csize;
119 char* ctype;
120 const CURLcode res = memfile_from_url((memfile_from_url_info){
121 .url = url,
122 .body = &mbody,
123 .body_writer = memfwrite,
124 .code = &code,
125 .csize = &csize,
126 .ctype = &ctype,
127 });
128 if (res != CURLE_OK || code != 200 || !mbody) {
129 if (error) *error = g_error_new_literal(G_FILE_ERROR, res, curl_easy_strerror(res));
130 free(ctype);
131 memfclose(mbody);
132 return NULL;
133 }
134
135 memfresize(mbody, csize >= 0 ? (size_t) csize : memfsize(mbody));
136
137 GdkPixbuf* const pixbuf = pixbuf_from_url_impl(ctype, mbody, error);
138
139 free(ctype);
140 memfclose(mbody);
141
142 return pixbuf;
143 }
144
145 GdkPixbuf*
pixbuf_from_url_as_file(const char * url,GError ** error)146 pixbuf_from_url_as_file(const char* url, GError** error) {
147 if (!url) return NULL;
148 gchar* newurl;
149 if (!strncmp(url, "x-growl-resource://", 19)) {
150 const gchar* const confdir = (const gchar*) g_get_user_config_dir();
151 gchar* const resourcedir = g_build_path(G_DIR_SEPARATOR_S, confdir, "gol", "resource", NULL);
152 newurl = g_build_filename(resourcedir, url + 19, NULL);
153 g_free(resourcedir);
154 } else
155 newurl = g_filename_from_uri(url, NULL, NULL);
156 GError* _error = NULL;
157 GdkPixbuf* const pixbuf = gdk_pixbuf_new_from_file(newurl ? newurl : url, &_error);
158 if (!pixbuf) gerror_set_or_free(error, _error);
159 g_free(newurl);
160 return pixbuf;
161 }
162
163