From 8352fb588b2aa96ff2efe652ac2f521c1bc3ee19 Mon Sep 17 00:00:00 2001 From: Storm Dragon Date: Sun, 27 Jul 2025 23:37:18 -0400 Subject: [PATCH] Found some old Identica references, removed that and it's legacy oauth v1 code. To my surprise, it actually still works. lol --- ttyverse.pl | 251 ++++++++++------------------------------------------ 1 file changed, 49 insertions(+), 202 deletions(-) diff --git a/ttyverse.pl b/ttyverse.pl index fe7afb3..6410aab 100755 --- a/ttyverse.pl +++ b/ttyverse.pl @@ -90,7 +90,7 @@ BEGIN { # === CONNECTION SETTINGS === # fediverseserver=mastodon.social # Your fediverse server # ssl=1 # Use SSL/HTTPS (recommended) -# authtype=oauth2 # Authentication type (oauth2 for fediverse) +# authtype=oauth2 # Always oauth2 (fediverse standard) # apibase= # Custom API base URL (auto-detected) # oauthbase= # Custom OAuth base URL (auto-detected) @@ -476,15 +476,9 @@ EOF # Validate OAuth credentials - OAuth 2.0 doesn't need tokensecret my $oauth_valid = 0; if (length($oauthkey) && length($oauthsecret) && length($tokenkey)) { - if (length($tokensecret)) { - # OAuth 1.0a format (has token secret) - $oauth_valid = 1; - $authtype = 'oauth1'; - } elsif (defined($oauth2_refresh_token) || !defined($tokensecret)) { - # OAuth 2.0 format (no token secret needed) - $oauth_valid = 1; - $authtype = 'oauth2'; - } + # Fediverse uses OAuth 2.0 format (no token secret needed) + $oauth_valid = 1; + $authtype = 'oauth2'; } die("** tried to load OAuth tokens from $keyfile\n". " but it seems corrupt or incomplete. please see the documentation,\n". @@ -950,9 +944,10 @@ exit(1) if (!&list_compile); ¬ify_compile; # check that we are using a sensible authtype, based on our guessed user agent -$authtype ||= "oauth2"; # Default to OAuth 2.0 for fediverse -die("** supported authtypes are basic, oauth, or oauth2 only.\n") -if ($authtype ne 'basic' && $authtype ne 'oauth' && $authtype ne 'oauth2'); +# Fediverse uses OAuth 2.0 exclusively +$authtype = "oauth2"; +die("** TTYverse only supports OAuth 2.0 (fediverse standard)\n") + if ($authtype ne 'oauth2'); if ($termrl) { $streamout = $stdout; # this is just simpler instead of dupping @@ -1257,34 +1252,12 @@ if ($lynx) { } $l .= "\n"; - # sign our request (Basic Auth, OAuth 1.0a, or OAuth 2.0) + # sign our request with OAuth 2.0 (fediverse standard) unless ($dont_do_auth) { - if ($authtype eq 'basic') { - $l .= "-u ".$mytoken.":".$mytokensecret."\n"; - } elsif ($authtype eq 'oauth2') { - # OAuth 2.0 Bearer token - my $bearer_header = &signrequest($resource, $data); - if ($bearer_header) { - $l .= $bearer_header . "\n"; - } - } else { - # OAuth 1.0a - my $nonce; - my $timestamp; - my $sig; - my $verifier = ''; - my $header; - my $ttoken = (length($mytoken) ? - (' oauth_token=\\"'.$mytoken.'\\",') : - ''); - - ($timestamp, $nonce, $sig, $verifier) = - &signrequest($resource, $data); - $header = <<"EOF"; --H "Authorization: OAuth oauth_nonce=\\"$nonce\\", oauth_signature_method=\\"HMAC-SHA1\\", oauth_timestamp=\\"$timestamp\\", oauth_consumer_key=\\"$oauthkey\\", oauth_signature=\\"$sig\\",${ttoken}${verifier} oauth_version=\\"1.0\\"" -EOF - print $stdout $header if ($superverbose); - $l .= $header; + # OAuth 2.0 Bearer token + my $bearer_header = &signrequest($resource, $data); + if ($bearer_header) { + $l .= $bearer_header . "\n"; } } @@ -8468,15 +8441,22 @@ sub postjson { $code = 0+$1; print $stdout $data if ($superverbose); - # 304 is actually a cop-out code and is not usually - # returned, so we should consider it a non-fatal error - if ($code == 304 || $code == 200 || $code == 204) { - &$exception(1, "*** warning: timeout or no data\n"); + # Handle successful HTTP responses properly for fediverse APIs + if ($code == 200) { + # 200 OK with content - continue processing + } elsif ($code == 204) { + # 204 No Content - successful but empty (normal for some API calls) + print $stdout "-- No new content (HTTP 204)\n" if ($verbose); + return []; + } elsif ($code == 304) { + # 304 Not Modified - no new content since last fetch + print $stdout "-- No new content since last fetch (HTTP 304)\n" if ($verbose); + return []; + } else { + &$exception(4, +"*** warning: unexpected HTTP return code $code from server\n"); return undef; } - &$exception(4, -"*** warning: unexpected HTTP return code $code from server\n"); - return undef; } # test for error/warning conditions with trivial case @@ -8581,15 +8561,22 @@ sub grabjson { $code = 0+$1; print $stdout $data if ($superverbose); - # 304 is actually a cop-out code and is not usually - # returned, so we should consider it a non-fatal error - if ($code == 304 || $code == 200 || $code == 204) { - &$exception(1, "*** warning: timeout or no data\n"); + # Handle successful HTTP responses properly for fediverse APIs + if ($code == 200) { + # 200 OK with content - continue processing + } elsif ($code == 204) { + # 204 No Content - successful but empty (normal for some API calls) + print $stdout "-- No new content (HTTP 204)\n" if ($verbose); + return []; + } elsif ($code == 304) { + # 304 Not Modified - no new content since last fetch + print $stdout "-- No new content since last fetch (HTTP 304)\n" if ($verbose); + return []; + } else { + &$exception(4, +"*** warning: unexpected HTTP return code $code from server\n"); return undef; } - &$exception(4, -"*** warning: unexpected HTTP return code $code from server\n"); - return undef; } # test for error/warning conditions with trivial case @@ -8853,6 +8840,10 @@ sub parsejson { # now verify the syntax tree. # the remaining stuff should just be enclosed in [ ], and only {}:, # for example, imagine if a bare semicolon were in this ... + # Special case: empty array is valid in fediverse APIs + if ($tdata eq "[]") { + return []; + } if ($tdata !~ s/^\[// || $tdata !~ s/\]$// || $tdata =~ /[^{}:,]/) { $tdata =~ s/'[^']*$//; # cut trailing strings if (($tdata =~ /^\[/ && $tdata !~ /\]$/) @@ -8862,7 +8853,7 @@ sub parsejson { return undef; } # it seems that :[], or :[]} should be accepted as valid in the syntax tree -# since identica uses this as possible for null properties +# since some APIs use this as possible for null properties # ,[], shouldn't be, etc. if ($tdata =~ /(^|[^:])\[\]($|[^},])/) { # oddity &$exception(11, "*** JSON warning: null list\n"); @@ -9834,156 +9825,12 @@ sub generate_nonce { unpack("H9000", pack("u", rand($$).$$.time())); } # Basic Auth. payload should already be URL encoded and *sorted*. # this is typically called by stringify_args to get authentication information. sub signrequest { - - # this horrible kludge is needed to account for both 5.005, or for - # 5.6+ installs with no stdlibs and just a bare Perl, both of which - # we support. I hope Larry Wall will forgive me for messing with - # compiler internals next time I see him at church. - BEGIN { $^H |= 0x00000008 unless ($] < 5.006); } - my $resource = shift; my $payload = shift; - # when we sign the initial request for an token, we obviously - # don't have one yet, so mytoken/mytokensecret can be null. - - my $nonce = &generate_nonce; - my @keybytes; - my $sig_base; - my $timestamp = time(); - return undef if ($authtype eq 'basic'); - - # OAuth 2.0 Bearer token support (Mastodon/fediverse) - if ($authtype eq 'oauth2') { - return undef if (!length($tokenkey)); - return "-H \"Authorization: Bearer $tokenkey\""; - } - - # OAuth 1.0a support (legacy fediverse) - return undef if (!length($oauthkey) || !length($oauthsecret)); - - (@keybytes) = map { ord($_) } - split(//, $oauthsecret.'&'.$mytokensecret); - if (ref($resource) eq 'ARRAY' || length($payload)) { - # split into _a and _b payloads lexically - my $payload_a = ''; - my $payload_b = ''; - my $payload_c = ''; # this is for a special case - my $w; - my $aorb = 0; - my $verifier = ''; - my $method = "GET"; - my $url; - - if (length($payload)) { - $method = "POST"; - # this is a bit problematic since it won't be - # sorted. we'll deal with this as we need to. - if (ref($resource) eq 'ARRAY') { - $url = &url_oauth_sub($resource->[0]); - $payload .= "&" . $resource->[1]; - } else { - $url = &url_oauth_sub($resource); - } - } elsif (ref($resource) eq 'ARRAY') { - $url = &url_oauth_sub($resource->[0]); - $payload = $resource->[1]; - } else { - $url = &url_oauth_sub($resource); - } - - # this is pretty simplistic but it's really all we need. - # the exception is oauth_verifier: that has to be wormed - # into the middle, and we assume it's just that. - if ($payload !~ /^oauth_verifier/) { - foreach $w (split(/\&/, $payload)) { - $aorb = 1 if - ($w =~ /^[p-z]/ || $w =~ /^o[b-z]/); - $w = &url_oauth_sub("${w}&"); - if ($aorb) { - $payload_b .= $w; - } else { - $payload_a .= $w; - } - } - } else { - $payload_c = &url_oauth_sub($payload) . "%26"; - $payload_a = $payload_b = ''; - $payload =~ s/^oauth_verifier=//; - $verifier = ' oauth_verifier=\\"' . $payload . '\\",'; - } - $payload_b =~ s/%26$//; - $sig_base = $method . "&" . - $url . "&" . - (length($payload_a) ? $payload_a : ''). - "oauth_consumer_key%3D" . $oauthkey . "%26" . - "oauth_nonce%3D" . $nonce . "%26" . - "oauth_signature_method%3DHMAC-SHA1%26" . - "oauth_timestamp%3D" . $timestamp . "%26" . - (length($mytoken) ? - ("oauth_token%3D" . $mytoken . "%26") : '') . - $payload_c . - "oauth_version%3D1.0" . - (length($payload_b) ? ("%26" . $payload_b) : ''); - } else { - $sig_base = "GET&" . - &url_oauth_sub($resource) . "&" . - "oauth_consumer_key%3D" . $oauthkey . "%26" . - "oauth_nonce%3D" . $nonce . "%26" . - "oauth_signature_method%3DHMAC-SHA1%26" . - "oauth_timestamp%3D" . $timestamp . "%26" . - (length($mytoken) ? - ("oauth_token%3D" . $mytoken . "%26") : '') . - $payload_c . # could be part of it - "oauth_version%3D1.0" ; - } - print $stdout -"token-secret: $mytokensecret\nconsumer-secret: $oauthsecret\nsig-base: $sig_base\n" - if ($superverbose); - return ($timestamp, $nonce, - &url_oauth_sub(&hmac_sha1($sig_base, @keybytes)), - $verifier); + # OAuth 2.0 Bearer token support (fediverse standard) + return undef if (!length($tokenkey)); + return "-H \"Authorization: Bearer $tokenkey\""; } -# this takes a token request and "tries hard" to get it. -sub tryhardfortoken { - my $url = shift; - my $body = shift; - my $tries = shift; - my $rawtoken; - $tries ||= 3; - - while($tries) { - my $i; - $rawtoken = &backticks($baseagent, '/dev/null', undef, - $url, $body, 0, @wend); - print $stdout ("token = $rawtoken\n") - if ($superverbose); - my (@keyarr) = split(/\&/, $rawtoken); - my $got_token = ''; - my $got_secret = ''; - foreach $i (@keyarr) { - my $key; - my $value; - - ($key, $value) = split(/\=/, $i); - $got_token = $value if ($key eq 'oauth_token'); - $got_secret = $value if ($key eq 'oauth_token_secret'); - } - if (length($got_token) && length($got_secret)) { - print $stdout " SUCCEEDED!\n"; - return ($got_token, $got_secret); - } - print $stdout "."; - $tries--; - } - print $stdout " FAILED!: \"$rawtoken\"\n"; -die("unable to fetch token. here are some possible reasons:\n". - " - root certificates are not updated (see documentation)\n". - " - you entered your authentication information wrong\n". - " - your computer's clock is not set correctly\n" . - " - server error\n" . - "fix these possible problems, or try again later.\n"); - exit; -} }