AmendHub

Download:

jcs

/

wallops

/

amendments

/

131

*: Add support for Pushover notifications when screen saver is running

The API options are input in a new settings window that will
eventually add more general options.

jcs made amendment 131 about 1 year ago
--- chatter.h Fri Sep 20 12:25:26 2024 +++ chatter.h Mon Sep 23 15:42:07 2024 @@ -40,7 +40,8 @@ #define FILE_MENU_ID 129 #define FILE_MENU_CONNECT_ID 1 -#define FILE_MENU_QUIT_ID 3 +#define FILE_MENU_SETTINGS_ID 2 +#define FILE_MENU_QUIT_ID 4 #define EDIT_MENU_ID 130 #define EDIT_MENU_CUT_ID 1 @@ -106,7 +107,7 @@ struct chatter { extern MenuHandle apple_menu, file_menu, edit_menu, view_menu, ignore_menu, window_menu; -void notify(void); +void notify(struct focusable *focusable, char *msg, char *titlefmt, ...); void cancel_notification(void); struct chatter * chatter_init(const char *server, --- http.c Mon Sep 23 15:45:49 2024 +++ http.c Mon Sep 23 15:45:49 2024 @@ -0,0 +1,643 @@ +/* + * Copyright (c) 2020-2022 joshua stein <jcs@jcs.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <stdio.h> +#include <string.h> +#include "dnr.h" +#include "http.h" +#include "util.h" + +char * http_user_agent(void); +struct http_request * http_init_req(const char *verb, const char *surl, + const char *headers, const char *body, size_t body_len); +ssize_t http_req_ingest(struct http_request *req); +size_t strappend(char *str, size_t str_size, size_t str_len, char *append); + +struct url * +url_parse(const char *str) +{ + struct url *url = NULL; + char *buf, *scheme, *host, *path; + unsigned short port; + short ret, pos; + size_t len, schemelen, hostlen, pathlen; + + len = strlen(str); + scheme = xmalloc(len + 1); + if (scheme == NULL) { + warn("http: Failed allocating %ld", len + 1); + return NULL; + } + host = xmalloc(len + 1); + if (host == NULL) { + warn("http: Failed allocating %ld", len + 1); + xfree(&scheme); + return NULL; + } + path = xmalloc(len + 1); + if (path == NULL) { + warn("http: Failed allocating %ld", len + 1); + xfree(&host); + xfree(&scheme); + return NULL; + } + + /* scheme://host:port/path */ + ret = sscanf(str, "%[^:]://%[^:]:%d%s%n", scheme, host, &port, path, + &pos); + if (ret == 4) { + if (pos > len) + panic("url_parse sscanf overflow"); + goto consolidate; + } + + /* scheme://host/path */ + ret = sscanf(str, "%[^:]://%[^/]%s%n", scheme, host, path, &pos); + if (ret == 3) { + if (pos > len) + panic("url_parse sscanf overflow"); + if (strcmp(scheme, "http") == 0) + port = 80; + else if (strcmp(scheme, "https") == 0) + port = 443; + else + goto cleanup; + goto consolidate; + } + + goto cleanup; + +consolidate: + schemelen = strlen(scheme); + hostlen = strlen(host); + pathlen = strlen(path); + + /* + * Put everything in a single chunk of memory so the caller can just + * free(url) + */ + len = sizeof(struct url) + schemelen + 1 + hostlen + 1 + pathlen + 1; + url = xmalloc(len); + if (url == NULL) { + warn("http: Failed allocating %ld for URL", len); + goto cleanup; + } + + url->scheme = (char *)url + sizeof(struct url); + len = strlcpy(url->scheme, scheme, schemelen + 1); + + url->host = url->scheme + len + 1; + len = strlcpy(url->host, host, hostlen + 1); + + url->path = url->host + len + 1; + len = strlcpy(url->path, path, pathlen + 1); + + url->port = port; + +cleanup: + xfree(&scheme); + xfree(&host); + xfree(&path); + + return url; +} + +ssize_t +url_encode(unsigned char *str, size_t strlimit, unsigned char *dst, + size_t dstsize) +{ + size_t len, n; + bool encode = false; + char a, b; + unsigned char c; + +encode: + for (n = 0, len = 0; str[n] != '\0' && (strlimit ? n < strlimit : 1); + n++) { + c = str[n]; + + if ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || + (c >= '0' && c <= '9') || c == '-' || c == '_' || c == '.' || + c == '~' || c == ' ') { + if (len >= dstsize) + return -1; + if (c == ' ') + c = '+'; + dst[len] = c; + len++; + } else { + if (len + 3 >= dstsize) + return -1; + sprintf((char *)dst + len, "%%%02X", c); + len += 3; + } + } + + dst[len] = '\0'; + return len; +} + +char * +http_user_agent(void) +{ + static char agent[32] = { 0 }; + + if (agent[0] == '\0') + snprintf(agent, sizeof(agent), "Amend/%s", get_version(false)); + + return agent; +} + +struct http_request * +http_init_req(const char *verb, const char *surl, const char *headers, + const char *body, size_t body_len) +{ + struct url *url; + struct http_request *req; + size_t len, alen, hlen; + short err; + char ip_s[16], trail[2], clength[30]; + char *oheaders = NULL, *mheaders = NULL; + ip_addr local_ip; + tcp_port local_port; + + _TCPInit(); + + url = url_parse(surl); + if (url == NULL) + return NULL; + + req = xmalloczero(sizeof(struct http_request)); + if (req == NULL) { + warn("http: Failed allocating http_request"); + return NULL; + } + req->url = url; + req->tcp_buf_size = (4 * 1500) + sizeof(req->chunk); + req->tcp_buf = xmalloc(req->tcp_buf_size); + if (req->tcp_buf == NULL) { + warn("http: Failed allocating tcp_buf"); + xfree(&req); + return NULL; + } + + err = _TCPCreate(&req->tcp_iopb, &req->tcp_stream, (Ptr)req->tcp_buf, + req->tcp_buf_size, nil, nil, nil, false); + if (err) { + warn("TCPCreate failed: %d", err); + goto error; + } + + err = DNSResolveName(req->url->host, &req->host_ip, NULL); + if (err) { + warn("Couldn't resolve host %s (%d)", req->url->host, err); + goto error; + } + + long2ip(req->host_ip, (char *)&ip_s); + + alen = 256 + strlen(req->url->host); + oheaders = xmalloc(alen); + if (oheaders == NULL) { + warn("http: Failed allocating headers"); + goto error; + } + + if (body_len) + snprintf(clength, sizeof(clength), + "Content-Length: %lu\r\n", body_len); + else + clength[0] = '\0'; + + hlen = snprintf(oheaders, alen, + "Host: %s\r\n" + "User-Agent: %s\r\n" + "Accept: */*\r\n" + "%s", + req->url->host, http_user_agent(), clength); + if (hlen > alen) { + warn("http: Truncated headers"); + goto error; + } + + if (headers != NULL) { + hlen = http_merge_headers(oheaders, headers, &mheaders); + xfree(&oheaders); + oheaders = mheaders; + mheaders = NULL; + } + + alen = 64 + strlen(req->url->path) + hlen; + req->message = xmalloc(alen); + if (req->message == NULL) { + warn("http: Failed allocating message"); + goto error; + } + + len = snprintf(req->message, alen, + "%s %s HTTP/1.0\r\n" + "%s" + "\r\n%s", verb, req->url->path, oheaders, body_len ? "\r\n" : ""); + xfree(&oheaders); + if (len > alen) { + warn("http: Truncated message"); + goto error; + } + + err = _TCPActiveOpen(&req->tcp_iopb, req->tcp_stream, req->host_ip, + req->url->port, &local_ip, &local_port, nil, nil, false); + if (err) { + warn("Failed connecting to %s (%s) port %d: %d", + req->url->host, ip_s, req->url->port, err); + goto error; + } + + memset(&req->tcp_wds, 0, sizeof(req->tcp_wds)); + req->tcp_wds[0].ptr = req->message; + req->tcp_wds[0].length = len; + + if (body_len) { + req->tcp_wds[1].ptr = (char *)body; + req->tcp_wds[1].length = body_len; + } + + err = _TCPSend(&req->tcp_iopb, req->tcp_stream, req->tcp_wds, nil, nil, + false); + if (err) { + warn("TCPSend to %s (%s) failed: %d", req->url->host, ip_s, err); + goto error; + } + + return req; + +error: + http_req_free(&req); + return NULL; +} + +struct http_request * +http_get(const char *surl, const char *headers) +{ + return http_init_req("GET", surl, headers, NULL, 0); +} + +struct http_request * +http_post(const char *surl, const char *headers, const char *body, + size_t body_len) +{ + return http_init_req("POST", surl, headers, body, body_len); +} + +size_t +http_merge_headers(const char *lheaders, const char *rheaders, + char **mheaders) +{ + /* rheaders take precedence */ + size_t lhlen, rhlen, mhlen; + size_t l, r, lastl, lastr; + char *h; + bool found; + + lhlen = strlen(lheaders); + rhlen = strlen(rheaders); + + h = *mheaders = xmalloczero(lhlen + rhlen + 1); + if (*mheaders == NULL) + panic("http: Failed allocating space for new headers"); + + /* add each in lheaders, unless it's in rheaders */ + mhlen = 0; + for (l = 0, lastl = 0; l < lhlen; l++) { + *h++ = lheaders[l]; + mhlen++; + + if (lheaders[l] != ':') + continue; + + found = false; + for (r = 0, lastr = 0; r < rhlen; r++) { + if (rheaders[r] == '\n') { + lastr = r + 1; + continue; + } + + if (rheaders[r] != ':') + continue; + + if (strncasecmp(lheaders + lastl, rheaders + lastr, + r - lastr + 1) == 0) { + /* take rvalue */ + found = true; + for (r = r + 1; r < rhlen; r++) { + *h++ = rheaders[r]; + mhlen++; + if (rheaders[r] == '\n') + break; + } + break; + } + } + + for (l = l + 1; l < lhlen; l++) { + if (!found) { + *h++ = lheaders[l]; + mhlen++; + } + if (lheaders[l] == '\n') + break; + } + lastl = l + 1; + } + + (*mheaders)[mhlen] = '\0'; + + /* now add each in rheaders, unless it's in lheaders */ + for (r = 0, lastr = 0; r < rhlen; r++) { + if (rheaders[r] != ':') + continue; + + found = false; + for (l = 0, lastl = 0; l < lhlen; l++) { + if (lheaders[l] == '\n') { + lastl = l + 1; + continue; + } + if (lheaders[l] != ':') + continue; + + if (strncasecmp(lheaders + lastl, rheaders + lastr, + l - lastl + 1) == 0) { + found = true; + break; + } + } + + for (r = lastr; r < rhlen; r++) { + if (!found) { + *h++ = rheaders[r]; + mhlen++; + } + if (rheaders[r] == '\n') + break; + } + lastr = r + 1; + } + + (*mheaders)[mhlen] = '\0'; + + return mhlen; +} + +ssize_t +http_req_ingest(struct http_request *req) +{ + short err; + unsigned short rlen; + + if (!req) + return -1; + + err = _TCPStatus(&req->tcp_iopb, req->tcp_stream, &req->tcp_status_pb, + nil, nil, false); + if (err || + req->tcp_status_pb.connectionState != ConnectionStateEstablished) + return -1; + + if (req->tcp_status_pb.amtUnreadData == 0) + return 0; + if (req->chunk_len == sizeof(req->chunk)) + return 0; + + rlen = MIN(req->tcp_status_pb.amtUnreadData, + sizeof(req->chunk) - req->chunk_len); + + err = _TCPRcv(&req->tcp_iopb, req->tcp_stream, + req->chunk + req->chunk_len, &rlen, nil, nil, false); + if (err) + return -1; + + req->chunk_len += rlen; + + return rlen; +} + +ssize_t +http_req_read_line(struct http_request *req, char *ret, size_t len) +{ + ssize_t n, copy = -1, skip = 0; + bool dead = false; + + for (;;) { + if (!dead && http_req_ingest(req) < 0) + dead = true; + + for (n = 1; n < req->chunk_len; n++) { + if (req->chunk[n - 1] == '\r' && req->chunk[n] == '\n') { + copy = n - 1; + skip = 2; + break; + } else if (n + 1 == len) { + copy = n + 1; + break; + } + } + + if (copy == -1 && dead && req->chunk_len < len) { + copy = req->chunk_len; + break; + } + if (copy >= 0) + break; + } + + if (ret != NULL && copy > 0) + memcpy(ret, req->chunk, copy); + + if (req->chunk_len <= copy + skip) + req->chunk_len = 0; + else { + memmove(req->chunk, req->chunk + copy + skip, + req->chunk_len - copy - skip); + req->chunk_len -= copy + skip; + } + + return copy; +} + +bool +http_req_skip_headers(struct http_request *req) +{ + ssize_t size; + size_t length; + char header[128]; + + for (;;) { + size = http_req_read_line(req, (char *)&header, sizeof(header)); + if (size < 0) + return false; + if (size == 0) + return true; + + if (size >= sizeof(header)) + size = sizeof(header) - 1; + header[size] = '\0'; + + if (strncmp(header, "Content-Length: ", 16) == 0) { + if (sscanf(header, "Content-Length: %ld", &length) == 1) + req->content_len = length; + } + } + + return false; +} + +void +http_req_free(void *reqptr) +{ + unsigned long *addr = (unsigned long *)reqptr; + void *ptr = (void *)*addr; + struct http_request *req = (struct http_request *)ptr; + + if (req == NULL) + return; + + _TCPRelease(&req->tcp_iopb, req->tcp_stream, nil, nil, false); + + if (req->message != NULL) + xfree(&req->message); + xfree(&req->tcp_buf); + xfree(&req->url); + xfree(&req); + + *addr = 0L; +} + +/* strlcat but passes a known length */ +size_t +strappend(char *str, size_t str_size, size_t str_len, char *append) +{ + if (str_len >= str_size - 1) + return str_len + strlen(append); + + while (*append != '\0') { + if (str_len < str_size - 1) { + str[str_len] = *append; + append++; + } + str_len++; + } + if (str_len < str_size - 1) + str[str_len] = '\0'; + else + str[str_size] = '\0'; + + return str_len; +} + +bool +pushover(char *api_token, char *user_key, char *options, char *title, + char *msg) +{ + static char http_body[1024], param[64]; + struct http_request *http; + char *curopt; + ssize_t len, n, lastn, blen, param_len; + char c; + + blen = strappend(http_body, sizeof(http_body), 0, "token="); + blen = strappend(http_body, sizeof(http_body), blen, api_token); + blen = strappend(http_body, sizeof(http_body), blen, "&user="); + blen = strappend(http_body, sizeof(http_body), blen, user_key); + + if (title && title[0]) { + blen = strappend(http_body, sizeof(http_body), blen, "&title="); + len = url_encode((unsigned char *)title, 0, + (unsigned char *)http_body + blen, sizeof(http_body) - blen); + if (len == -1) + return false; + blen += len; + } + + blen = strappend(http_body, sizeof(http_body), blen, "&message="); + + if (blen >= sizeof(http_body)) + return false; + + len = url_encode((unsigned char *)msg, 0, + (unsigned char *)http_body + blen, sizeof(http_body) - blen); + if (len == -1) + return false; + blen += len; + + if (blen >= sizeof(http_body)) + return false; + + param[0] = '\0'; + + for (n = 0, param_len = 0; options && options[0]; n++) { + c = options[n]; + + if (c == '=' || c == ',' || c == '\0') { + /* remove trailing space */ + param[param_len] = '\0'; + while (param_len > 0 && param[param_len - 1] == ' ') { + param[param_len - 1] = '\0'; + param_len--; + } + + if (c == '=') + blen = strappend(http_body, sizeof(http_body), blen, + "&"); + else if (c == ',' || c == '\0') + blen = strappend(http_body, sizeof(http_body), blen, + "="); + + if (param_len) { + if (blen >= sizeof(http_body)) + return false; + len = url_encode((unsigned char *)param, param_len, + (unsigned char *)http_body + blen, + sizeof(http_body) - blen); + if (len == -1) + return false; + blen += len; + } + + if (c == '\0') + break; + + param_len = 0; + param[0] = '\0'; + } else if (param_len < sizeof(param)) { + if (c == ' ' && param[0] == '\0') + continue; + param[param_len++] = c; + } else + return false; + } + + blen = strappend(http_body, sizeof(http_body), blen, "\r\n"); + if (blen > sizeof(http_body)) + return false; + + http = http_post("http://api.pushover.net/1/messages.json", + "Content-type: application/x-www-form-urlencoded", http_body, + blen); + http_req_skip_headers(http); + http_req_free(&http); + + return true; +} --- http.h Mon Sep 23 15:31:26 2024 +++ http.h Mon Sep 23 15:31:26 2024 @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2020-2022 joshua stein <jcs@jcs.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef __HTTP_H__ +#define __HTTP_H__ + +#include "util.h" +#include "tcp.h" + +struct url { + char *scheme; + char *host; + unsigned short port; + char *path; +}; + +struct http_request { + struct url *url; + ip_addr host_ip; + + TCPiopb tcp_iopb; + StreamPtr tcp_stream; + unsigned char *tcp_buf; + size_t tcp_buf_size; + wdsEntry tcp_wds[3]; + TCPStatusPB tcp_status_pb; + + char *message; + + size_t content_len; + short response_code; + + char chunk[2048]; + ssize_t chunk_len; + ssize_t chunk_off; +}; + +struct url * url_parse(const char *str); +ssize_t url_encode(unsigned char *str, size_t strlimit, unsigned char *dst, + size_t dstsize); + +size_t http_merge_headers(const char *lheaders, const char *rheaders, + char **mheaders); +struct http_request * http_get(const char *surl, const char *headers); +struct http_request * http_post(const char *surl, const char *headers, + const char *body, size_t body_len); +ssize_t http_req_read_line(struct http_request *req, char *ret, size_t len); +bool http_req_skip_headers(struct http_request *req); +void http_req_free(void *reqptr); + +bool pushover(char *api_token, char *user_key, char *options, char *title, + char *msg); + +#endif \ No newline at end of file --- irc.c Sun Sep 22 17:14:39 2024 +++ irc.c Mon Sep 23 15:41:48 2024 @@ -563,8 +563,8 @@ irc_process_server(struct irc_connection *conn) chatter_printf(conn->chatter, conn, user->nick, "$B[%s($0%s@%s$B)]$0$/ %s", user->nick, user->username, user->hostname, msg.msg); - if (!conn->chatter->focusable->visible) - notify(); + notify(conn->chatter->focusable, msg.msg, + "Private message from %s", user->nick); } else { /* message to channel we're in */ char *us; @@ -588,8 +588,8 @@ irc_process_server(struct irc_connection *conn) chatter_printf(conn->chatter, conn, msg.arg[0], "<$U%s$0>$/ %s", user->nick, msg.msg); - if (!conn->chatter->focusable->visible) - notify(); + notify(conn->chatter->focusable, msg.msg, + "Mention in %s by %s", msg.arg[0], user->nick); } else chatter_printf(conn->chatter, conn, msg.arg[0], "$/<%s> %s", @@ -1071,8 +1071,8 @@ irc_process_server(struct irc_connection *conn) chatter_printf(conn->chatter, conn, NULL, "$B***$0 $B%s$0$/ %s online", msg.msg, strstr(msg.msg, ",") ? "are" : "is"); - if (conn->chatter->focusable && !conn->chatter->focusable->visible) - notify(); + if (conn->chatter->focusable) + notify(conn->chatter->focusable, NULL, NULL); return true; case 731: /* monitor offline */ --- main.c Fri Sep 20 20:55:14 2024 +++ main.c Mon Sep 23 15:45:39 2024 @@ -17,6 +17,7 @@ #include <string.h> #include <stdarg.h> #include "chatter.h" +#include "http.h" #include "irc.h" #include "settings.h" #include "tcp.h" @@ -218,7 +219,7 @@ show_connect_dialog(void) { bool valid; - valid = settings_edit(!settings_load()); + valid = connect_dialog(!settings_load()); update_menu(); if (!valid) @@ -263,6 +264,10 @@ handle_menu(long menu_id) show_connect_dialog(); ret = true; break; + case FILE_MENU_SETTINGS_ID: + settings_dialog(!settings_load()); + ret = true; + break; case FILE_MENU_QUIT_ID: ret = true; if (focusables_quit()) @@ -471,14 +476,31 @@ handle_exit(void) } void -notify(void) +notify(struct focusable *focusable, char *msg, char *titlefmt, ...) { - memset(&notification, 0, sizeof(notification)); - notification.qType = nmType; - notification.nmMark = 1; - notification.nmSound = (Handle)-1; - notification.nmIcon = GetResource('SICN', NOTIFICATION_ICON_ID); - NMInstall(&notification); + static char title[250]; + va_list argptr; + + if (focusable && !focusable->visible) { + memset(&notification, 0, sizeof(notification)); + notification.qType = nmType; + notification.nmMark = 1; + notification.nmSound = (Handle)-1; + notification.nmIcon = GetResource('SICN', NOTIFICATION_ICON_ID); + NMInstall(&notification); + } + + if (msg && screen_saver_running()) { + va_start(argptr, titlefmt); + vsnprintf(title, sizeof(title), titlefmt, argptr); + va_end(argptr); + + if (settings.pushover_api_token[0] && settings.pushover_user_key[0]) + pushover(settings.pushover_api_token, + settings.pushover_user_key, settings.pushover_options, title, + msg); + /* otherwise maybe stop the screen saver? */ + } } void --- settings.c Fri Sep 20 20:44:55 2024 +++ settings.c Mon Sep 23 14:10:12 2024 @@ -51,42 +51,61 @@ struct setting { unsigned long offset; unsigned long size; char password_storage[256]; -} settings_defs[] = { +}; + +struct setting connect_defs[] = { { "Server Name", SETTING_STRING, 0, 1, 0, - DEFAULT_SERVER_NAME, SETTINGS_SERVER_ID, + DEFAULT_SERVER_NAME, CONNECT_SERVER_ID, offsetof(struct settings, server), member_size(struct settings, server) }, { "Server Port", SETTING_USHORT, 0, 1, 65535, - DEFAULT_PORT, SETTINGS_PORT_ID, + DEFAULT_PORT, CONNECT_PORT_ID, offsetof(struct settings, port), member_size(struct settings, port) }, { "Server Password", SETTING_PASSWORD, 0, 0, 0, - "", SETTINGS_PASSWORD_ID, + "", CONNECT_PASSWORD_ID, offsetof(struct settings, password), member_size(struct settings, password) }, { "Nickname", SETTING_STRING, 0, 1, 0, - "", SETTINGS_NICK_ID, + "", CONNECT_NICK_ID, offsetof(struct settings, nick), member_size(struct settings, nick) }, { "Ident", SETTING_STRING, 0, 1, 0, - DEFAULT_IDENT, SETTINGS_IDENT_ID, + DEFAULT_IDENT, CONNECT_IDENT_ID, offsetof(struct settings, ident), member_size(struct settings, ident) }, { "Realname", SETTING_STRING, 0, 1, 0, - DEFAULT_REALNAME, SETTINGS_REALNAME_ID, + DEFAULT_REALNAME, CONNECT_REALNAME_ID, offsetof(struct settings, realname), member_size(struct settings, realname) }, { "Hide MOTD", SETTING_BOOL, 0, 0, 1, - DEFAULT_HIDE_MOTD, SETTINGS_HIDE_MOTD_ID, + DEFAULT_HIDE_MOTD, CONNECT_HIDE_MOTD_ID, offsetof(struct settings, hide_motd), member_size(struct settings, hide_motd) }, { "Channel", SETTING_STRING, 0, 0, 0, - DEFAULT_CHANNEL, SETTINGS_CHANNEL_ID, + DEFAULT_CHANNEL, CONNECT_CHANNEL_ID, offsetof(struct settings, channel), member_size(struct settings, channel) }, }; +struct setting settings_defs[] = { + { "Pushover API Token", SETTING_STRING, 0, 0, 30, + "", SETTINGS_PUSHOVER_API_ID, + offsetof(struct settings, pushover_api_token), + member_size(struct settings, pushover_api_token) }, + { "Pushover User Key", SETTING_STRING, 0, 0, 30, + "", SETTINGS_PUSHOVER_USER_ID, + offsetof(struct settings, pushover_user_key), + member_size(struct settings, pushover_user_key) }, + { "Other Pushover Options", SETTING_STRING, 0, 0, 0, + "", SETTINGS_PUSHOVER_OPTS_ID, + offsetof(struct settings, pushover_options), + member_size(struct settings, pushover_options) }, +}; + void settings_find_prefs_folder(short *vrefnum, long *dirid); +bool settings_edit(struct setting *defs, size_t defs_count, short dlog_id, + bool use_defaults); bool settings_load(void) @@ -196,8 +215,23 @@ create_failed: } bool -settings_edit(bool use_defaults) +connect_dialog(bool use_defaults) { + return settings_edit(connect_defs, nitems(connect_defs), + CONNECT_DLOG_ID, use_defaults); +} + +bool +settings_dialog(bool use_defaults) +{ + return settings_edit(settings_defs, nitems(settings_defs), + SETTINGS_DLOG_ID, use_defaults); +} + +bool +settings_edit(struct setting *defs, size_t defs_count, short dlog_id, + bool use_defaults) +{ struct settings tsettings = settings; struct setting *s; DialogTHndl dlgh; @@ -212,18 +246,18 @@ settings_edit(bool use_defaults) Str255 stmp; /* center dialog in screen */ - dlgh = (DialogTHndl)xGetResource('DLOG', SETTINGS_DLOG_ID); + dlgh = (DialogTHndl)xGetResource('DLOG', dlog_id); HLock(dlgh); center_in_screen((**dlgh).boundsRect.right - (**dlgh).boundsRect.left, (**dlgh).boundsRect.bottom - (**dlgh).boundsRect.top, true, &(**dlgh).boundsRect); HUnlock(dlgh); - if ((dlg = GetNewDialog(SETTINGS_DLOG_ID, nil, (WindowPtr)-1)) == NULL) - panic("Can't find settings DLOG %d", SETTINGS_DLOG_ID); + if ((dlg = GetNewDialog(dlog_id, nil, (WindowPtr)-1)) == NULL) + panic("Can't find settings DLOG %d", dlog_id); - for (n = 0; n < nitems(settings_defs); n++) { - s = &settings_defs[n]; + for (n = 0; n < defs_count; n++) { + s = &defs[n]; sdata = (char *)&tsettings + s->offset; GetDItem(dlg, s->ditl_id, &itype, &ihandle, &irect); @@ -303,8 +337,8 @@ get_input: save = false; verify: - for (n = 0; n < nitems(settings_defs); n++) { - s = &settings_defs[n]; + for (n = 0; n < defs_count; n++) { + s = &defs[n]; sdata = (char *)&tsettings + s->offset; GetDItem(dlg, s->ditl_id, &itype, &ihandle, &irect); --- settings.h Tue Sep 10 22:04:44 2024 +++ settings.h Mon Sep 23 12:55:14 2024 @@ -22,15 +22,15 @@ #define SETTINGS_FILENAME "\pWallops Preferences" #define SETTINGS_FILE_CREATOR 'WLOP' -#define SETTINGS_DLOG_ID 129 -#define SETTINGS_SERVER_ID 2 -#define SETTINGS_PORT_ID 3 -#define SETTINGS_PASSWORD_ID 4 -#define SETTINGS_NICK_ID 5 -#define SETTINGS_IDENT_ID 6 -#define SETTINGS_REALNAME_ID 7 -#define SETTINGS_HIDE_MOTD_ID 8 -#define SETTINGS_CHANNEL_ID 9 +#define CONNECT_DLOG_ID 128 +#define CONNECT_SERVER_ID 2 +#define CONNECT_PORT_ID 3 +#define CONNECT_PASSWORD_ID 4 +#define CONNECT_NICK_ID 5 +#define CONNECT_IDENT_ID 6 +#define CONNECT_REALNAME_ID 7 +#define CONNECT_HIDE_MOTD_ID 8 +#define CONNECT_CHANNEL_ID 9 #define DEFAULT_SERVER_NAME "irc.libera.chat" #define DEFAULT_PORT "6667" @@ -39,6 +39,11 @@ #define DEFAULT_CHANNEL "#cyberpals" #define DEFAULT_HIDE_MOTD "0" +#define SETTINGS_DLOG_ID 129 +#define SETTINGS_PUSHOVER_API_ID 2 +#define SETTINGS_PUSHOVER_USER_ID 3 +#define SETTINGS_PUSHOVER_OPTS_ID 4 + struct settings { short version; #define SETTINGS_VERSION 1 @@ -51,12 +56,16 @@ struct settings { char channel[32]; unsigned char hide_motd; unsigned long ignores; + char pushover_api_token[32]; + char pushover_user_key[32]; + char pushover_options[256]; }; extern struct settings settings; bool settings_load(void); void settings_save(struct settings *tsettings); -bool settings_edit(bool use_defaults); +bool connect_dialog(bool use_defaults); +bool settings_dialog(bool use_defaults); #endif \ No newline at end of file --- tcp.c Fri Aug 30 09:49:55 2024 +++ tcp.c Mon Sep 23 15:19:30 2024 @@ -197,7 +197,7 @@ _TCPActiveOpen(TCPiopb *pb, StreamPtr stream, ip_addr pb->tcpStream = stream; pb->ioResult = 1; - pb->csParam.open.ulpTimeoutValue = 30; + pb->csParam.open.ulpTimeoutValue = 10; pb->csParam.open.ulpTimeoutAction = 1; pb->csParam.open.validityFlags = 0xC0; #if 0 --- wallops.π.r Sun Sep 22 17:36:59 2024 +++ wallops.π.r Tue Sep 24 12:40:08 2024 @@ -5,8 +5,9 @@ data 'MENU' (128) { }; data 'MENU' (129) { - $"0081 0000 0000 0000 0000 FFFF FFFB 0446" /* .Å.............F */ + $"0081 0000 0000 0000 0000 FFFF FFF7 0446" /* .Å.............F */ $"696C 650A 436F 6E6E 6563 742E 2E2E 004E" /* ile.Connect....N */ + $"0000 0B53 6574 7469 6E67 732E 2E2E 0000" /* ...Settings..... */ $"0000 012D 0000 0000 0451 7569 7400 5100" /* ...-.....Quit.Q. */ $"0000" /* .. */ }; @@ -54,26 +55,26 @@ data 'DITL' (128) { }; data 'DITL' (129, "CONNECT_DITL") { - $"0010 0000 0000 00FA 0096 010E 00E0 0407" /* .........ñ...... */ + $"0010 0000 0000 00D7 0096 00EB 00E0 0407" /* .......◊.ñ...... */ $"436F 6E6E 6563 7400 0000 0000 000A 0096" /* Connect........ñ */ - $"001A 0144 1000 0000 0000 0028 0096 0038" /* ...D.......(.ñ.8 */ - $"00CA 1004 3636 3637 0000 0000 0046 0096" /* . ..6667.....F.ñ */ - $"0056 00F4 1000 0000 0000 0064 0096 0074" /* .V.........d.ñ.t */ - $"00F4 1000 0000 0000 0082 0096 0092 00F4" /* .........Ç.ñ.í.. */ - $"1000 0000 0000 00A0 0096 00B0 0144 1000" /* .......†.ñ.∞.D.. */ - $"0000 0000 00BE 0096 00D0 00B4 0500 0000" /* .....æ.ñ.–.¥.... */ - $"0000 00DC 0096 00EC 0144 1000 0000 0000" /* .....ñ...D...... */ + $"001A 0144 1000 0000 0000 0023 0096 0033" /* ...D.......#.ñ.3 */ + $"00CA 1004 3636 3637 0000 0000 003C 0096" /* . ..6667.....<.ñ */ + $"004C 00F4 1000 0000 0000 0055 0096 0065" /* .L.........U.ñ.e */ + $"00F4 1000 0000 0000 006E 0096 007E 00F4" /* .........n.ñ.~.. */ + $"1000 0000 0000 0087 0096 0097 0144 1000" /* .......á.ñ.ó.D.. */ + $"0000 0000 00A0 0096 00B2 00B4 0500 0000" /* .....†.ñ.≤.¥.... */ + $"0000 00B9 0096 00C9 0144 1000 0000 0000" /* ...π.ñ.….D...... */ $"000A 000A 001A 0055 8807 5365 7276 6572" /* .......Uà.Server */ - $"3A72 0000 0000 0028 000A 0038 005E 880C" /* :r.....(...8.^à. */ + $"3A72 0000 0000 0023 000A 0033 005E 880C" /* :r.....#...3.^à. */ $"5365 7276 6572 2050 6F72 743A 0000 0000" /* Server Port:.... */ - $"0046 000A 0056 0089 8810 5365 7276 6572" /* .F...V.âà.Server */ - $"2050 6173 7377 6F72 643A 0000 0000 0064" /* Password:.....d */ - $"000A 0074 0055 8805 4E69 636B 3A3A 0000" /* ...t.Uà.Nick::.. */ - $"0000 0082 000A 0092 0055 8806 4964 656E" /* ...Ç...í.Uà.Iden */ - $"743A 0000 0000 00A0 000A 00B0 0055 880A" /* t:.....†...∞.Uà. */ - $"5265 616C 204E 616D 653A 0000 0000 00BE" /* Real Name:.....æ */ - $"000A 00CE 0055 880A 4869 6465 204D 4F54" /* ...Œ.Uà.Hide MOT */ - $"443A 0000 0000 00DC 000A 00ED 0072 880D" /* D:...........rଠ*/ + $"003C 000A 004C 0089 8810 5365 7276 6572" /* .<...L.âà.Server */ + $"2050 6173 7377 6F72 643A 0000 0000 0055" /* Password:.....U */ + $"000A 0065 0055 8805 4E69 636B 3A3A 0000" /* ...e.Uà.Nick::.. */ + $"0000 006E 000A 007E 0055 8806 4964 656E" /* ...n...~.Uà.Iden */ + $"743A 0000 0000 0087 000A 0097 0055 880A" /* t:.....á...ó.Uà. */ + $"5265 616C 204E 616D 653A 0000 0000 00A0" /* Real Name:.....† */ + $"000A 00B0 0055 880A 4869 6465 204D 4F54" /* ...∞.Uà.Hide MOT */ + $"443A 0000 0000 00B9 000A 00CA 0072 880D" /* D:.....π... .rଠ*/ $"4A6F 696E 2043 6861 6E6E 656C 3A6E" /* Join Channel:n */ }; @@ -84,15 +85,34 @@ data 'DITL' (130, "ABOUT_DITL", purgeable, preload) { $"0000 0000 FFD8 0106 011E C002 C180" /* .....ÿ....¿.¡Ä */ }; -data 'DLOG' (129, "CONNECT_DLOG") { - $"0034 0056 0149 01A7 0004 0000 0100 0000" /* .4.V.I.ß........ */ - $"0000 0081 1143 6F6E 6E65 6374 2074 6F20" /* ...Å.Connect to */ - $"5365 7276 6572" /* Server */ +data 'DITL' (131, "SETTINGS_DITL") { + $"0006 0000 0000 005A 00A1 006E 00EB 0404" /* .......Z.°.n.... */ + $"5361 7665 0000 0000 000A 00BE 001A 017E" /* Save.......æ...~ */ + $"1000 0000 0000 0023 00BE 0033 017E 1000" /* .......#.æ.3.~.. */ + $"0000 0000 003C 00BE 004C 017E 1000 0000" /* .....<.æ.L.~.... */ + $"0000 000A 000A 001A 009F 8813 5075 7368" /* .........üà.Push */ + $"6F76 6572 2041 5049 2054 6F6B 656E 3A12" /* over API Token:. */ + $"0000 0000 0023 000A 0034 0096 8812 5075" /* .....#...4.ñà.Pu */ + $"7368 6F76 6572 2055 7365 7220 4B65 793A" /* shover User Key: */ + $"0000 0000 003C 000A 004D 00B2 8817 4F74" /* .....<...M.≤à.Ot */ + $"6865 7220 5075 7368 6F76 6572 204F 7074" /* her Pushover Opt */ + $"696F 6E73 3A00" /* ions:. */ }; +data 'DLOG' (129, "SETTINGS_DLOG") { + $"006A 0040 00E0 01CA 0004 0000 0100 0000" /* .j.@... ........ */ + $"0000 0083 0853 6574 7469 6E67 73" /* ...É.Settings */ +}; + data 'DLOG' (130, "ABOUT") { $"0032 0024 0120 01CB 0001 0000 0000 0000" /* .2.$. .À........ */ $"0000 0082 0000 280A" /* ...Ç..(. */ +}; + +data 'DLOG' (128, "CONNECT_DLOG") { + $"0034 0056 0128 01AA 0004 0000 0100 0000" /* .4.V.(.™........ */ + $"0000 0081 1143 6F6E 6E65 6374 2074 6F20" /* ...Å.Connect to */ + $"5365 7276 6572" /* Server */ }; data 'vers' (1) {