X-Git-Url: http://pere.pagekite.me/gitweb/homepage.git/blobdiff_plain/0a5e76ebbf8c437aa0646f5c9c48ddf1f39fbad5..5876f6fd93ae77a222af1d7a289f06c2d9fe8ce7:/blog/archive/2023/11/11.rss diff --git a/blog/archive/2023/11/11.rss b/blog/archive/2023/11/11.rss index 38aa11e92e..a9db836f25 100644 --- a/blog/archive/2023/11/11.rss +++ b/blog/archive/2023/11/11.rss @@ -3,13 +3,1050 @@ Petter Reinholdtsen - Entries from November 2023 Entries from November 2023 - https://people.skolelinux.org/pere/blog/ + http://www.hungry.com/~pere/blog/ + + «Når «på» blir «pÃ¥»: Et reservoar av tegn sett fra depotet» i tidsskriftet Aksess + http://www.hungry.com/~pere/blog/_N_r__p___blir__p_____Et_reservoar_av_tegn_sett_fra_depotet__i_tidsskriftet_Aksess.html + http://www.hungry.com/~pere/blog/_N_r__p___blir__p_____Et_reservoar_av_tegn_sett_fra_depotet__i_tidsskriftet_Aksess.html + Wed, 15 Nov 2023 09:20:00 +0100 + <p>For noen uker siden skrev en kamerat og meg +<a href="https://www.aksess-tidsskrift.no/fordypning/175530">en +artikkel om tegnsett</a> i +<a href="https://www.aksess-tidsskrift.no/">arkivtidsskriftet +Aksess</a> både på web og i papirutgave nr. 3 2023. Her er det som +nettopp ble publisert.</p> + +<blockquote> + +<p><strong>Når «på» blir «pÃ¥»: Et reservoar av tegn sett fra +depotet</strong></p> + +<p>av Thomas Sødring og Petter Reinholdtsen</p> + +<p>De færreste av oss tenker over hva som skjer dypere i datamaskinen +mens vi sitter der og skriver noe på tastaturet. Når du trykker på +tasten «Å», så vises bokstaven Å. Men noen ganger blir det +feil. Hvorfor det – og hva er viktig å være klar over i +arkivsammenheng?</p> + +<p>Dersom bokstaver tolkes forskjellig mellom systemer, blir det fort +rot, dette kalles mojibake blant kjennere, etter det japanske +uttrykket for tegnomforming. Det er en lang historie her som tidvis +har vært preget av rot. Noen husker kanskje tilbake til en tid der +bokstavene æ, ø og å ofte var ødelagt i e-poster – et klassisk +eksempel på tegnsettproblemstilling.</p> + +<p id="tegnsett_access_nå_og_før"><strong>«Nå» og «før»</strong></p> + +<p>Tid er et skjult problem for depot fordi vi danner dokumentasjon i +en kontekst som er preget av å være «nå». Vår forståelse av verden og +bruken av teknologi er utgangspunktet for denne konteksten. Tenk selv +hvordan verden har utviklet seg de siste 20 årene, hva samfunnet er +opptatt av, og hvordan vi bruker teknologi i hverdagen. Tid er et +skjult problem fordi når vi trekker dokumentasjon ut av systemer og +deponerer for langtidsbevaring, er konteksten til materialet «nå», men +verden går videre. Ettersom teknologien og måten vi bruker den på, +utvikler seg, blir «nå» til «før», og dokumentasjonen befinner seg +snart i en «før»-kontekst.</p> + +<p>Dette med «før» og «nå» i forhold til dokumentasjonens kontekst er +noe vi er veldig lite bevisste på, men det er en problemstilling +depotarkivene eier og forvalter. En av disse utfordringene er hvorfor +«Ø» ikke nødvendigvis er det samme som «Ø», og hvorfor det i det hele +tatt gir mening å si noe sånt. Vi snakker her om noe som heter +tegnsett, som er en avtalt måte å representere bokstaver, tall og +andre symboler på slik at vi på en feilfri måte kan utveksle tekst +mellom datasystemer.</p> + +<p>Tegnsettproblemstillingen er satt sammen av fire fasetter; +repertoar, representasjon, koding og uttegning.</p> + +<p id="tegnsett_access_repertoarer"><strong>Repertoarer</strong></p> + +<p>Repertoar er en samling med tegn og symboler som kan +representeres. Tenk norsk alfabet eller japanske piktogrammer, men +også matematiske og elektroniske symboler. Bokstaven «stor a» kan være +en oppføring i et slikt repertoar. For å kunne brukes i en datamaskin +trenger hver oppføring i et slikt repertoar en representasjon, hvilket +i datamaskinsammenheng betyr at det tilordnes et tall. Tallet kan +lagres på ulike vis i en eller flere kodingsformater. For eksempel kan +en skrive tallet ti som både 10, X og A, i henholdsvis +titallssystemet, romertallssystemet og sekstentallssystemet.</p> + +<p>Hvis en skal kunne lese inn filer og vite hvilket tall og hvilken +representasjon og instans i et repertoar det er snakk om, så må en +vite hvordan tallet er kodet. Sist, men ikke minst, for å kunne bruke +symbolet til noe må det kunne være kjent hvordan det skal se ut eller +tegnes på ark. Det finnes utallige skrifttyper med norske bokstaver, +alle litt forskjellige, og skal en kunne tegne en stor A på skjermen, +så må datamaskinen vite hva den skal tegne. Skrifttyper inneholder +informasjon om hvordan ulike tall skal tegnes. De inneholder ikke +alltid alle symbolene som er brukt i en tekst, hvilket gjør at ikke +alle forståtte tegn vil kunne vises på skjerm eller ark.</p> + +<p>Hver av disse fasettene må være avklart for å kunne ta vare på og vise +frem tekst med en datamaskin. Kombinasjon av repertoar, representasjon +og koding er det en kaller et tegnsett. Kombinasjonen av +representasjon og uttegning kalles en skrifttype. De fleste +skrifttyper har også informasjon om repertoar, men det finnes +skrifttyper som kun kobler mellom tallkode og uttegning, uten å +fortelle noe om hvordan tallkodene egentlig skal tolkes.</p> + +<p id="tegnsett_access_fra_ascii_til_iso_8859"><strong>Fra ASCII til ISO-8859</strong></p> + +<p>Vi begynner historien med ASCII (American Standard Code for +Information Interchange) som har en historie som spores tilbake til +1963. Utgangspunktet til ASCII var at det kunne kode opp til 128 +forskjellige symboler i vanlig bruk i USA. De visuelle symbolene i +ASCII er de små og store bokstavene (a til z og A til Z), tall (0 til +9) og tegnsettingssymboler (for eksempel semikolon, komma og +punktum). ASCII har også noen usynlige symboler som ble brukt for +bl.a. kommunikasjon. Før ASCII var det for eksempel teleks-tegnsett +med plass til bare 32 tegn og EBCDIC med plass til 256 tegn, alle med +en helt annen rekkefølge på symbolene enn ASCII, men de har vært lite +brukt de siste femti årene. Et eksempel på noen utvalgte symboler i +repertoaret til ASCII vises i tabell 1.</p> + +<table align="center" width="50%"> + +<caption>Tabell 1. Eksempel på utvalgte symboler hentet fra +ASCII-tegnsettet. Kolonnen «Binær» viser symbolets verdi i +totallssystemet (1 og 0 tall), mens kolonnen «Desimal» viser symbolets +verdi i titallssystemet.</caption> + +<tbody> +<tr> +<th>Grafisk</th> +<th>Binær</th> +<th>Desimal</th> +</tr> +<tr> +<td>A</td> +<td>1000001</td> +<td align="right">65</td> +</tr> +<tr> +<td>M</td> +<td>1001101</td> +<td align="right">77</td> +</tr> +<tr> +<td>Z</td> +<td>1011010</td> +<td align="right">90</td> +</tr> +<tr> +<td>a</td> +<td>1100001</td> +<td align="right">97</td> +</tr> +<tr> +<td>m</td> +<td>1101101</td> +<td align="right">109</td> +</tr> +<tr> +<td>z</td> +<td>1111010</td> +<td align="right">122</td> +</tr> +<tr> +<td>0</td> +<td>0110000</td> +<td align="right">48</td> +</tr> +<tr> +<td>9</td> +<td>0111001</td> +<td align="right">58</td> +</tr> +<tr> +<td>;</td> +<td>0111011</td> +<td align="right">59</td> +</tr> +</tbody> +</table> + +<p>Det opprinnelige ASCII-tegnsettet ble også omtalt som ASCII-7 og +brukte 7 bits (0 og 1) for å representere symboler. Datamaskiner er +ofte konfigurert til å jobbe med enheter der bits er gruppert som 4 +eller 8 bits . Det lå en mulighet i å ta i bruk bit åtte. En slik +endring ville gjøre det mulig for datamaskiner å øke antall symboler +de kunne representere, noe som ga en økning fra 128 forskjellige +symboler til 256 forskjellige symboler. Det ble åpnet for å innlemme +de nordiske bokstavene sammen med ASCII, og dette ble etter hvert +standardisert som ISO-8859-1. Tabell 2 viser deler av ISO-8859-1 som +støtter de norske bokstavene.</p> + +<p>Det sier seg selv at muligheten til å representere inntil 256 symboler +ikke holder når vi snakker om en global verden, og det ble gjort et +standardiseringsløp som tok utgangspunkt i ASCII-7 med en utvidelse +til å bruke den åttende biten for ulike språkgrupper. Denne standarden +heter ISO-8859 og er inndelt i opptil 16 varianter, altså fra +ISO-8859-1 til ISO-8859-16.</p> + +<table align="center" width="50%"> + +<caption>Tabell 2. Koding av de norske symbolene slik de er definert i +ISO-8859-1 tegnsettet.</caption> + +<tbody> +<tr> +<th>Grafisk</th> +<th>Binær</th> +<th>Desimal</th> +</tr> +<tr> +<td>Æ</td> +<td>11000110</td> +<td align="right">198</td> +</tr> +<tr> +<td>Ø</td> +<td>11011000</td> +<td align="right">216</td> +</tr> +<tr> +<td>Å</td> +<td>11000101</td> +<td align="right">197</td> +</tr> +<tr> +<td>æ</td> +<td>11100110</td> +<td align="right">230</td> +</tr> +<tr> +<td>ø</td> +<td>11111000</td> +<td align="right">248</td> +</tr> +<tr> +<td>å</td> +<td>11100101</td> +<td align="right">229</td> +</tr> +</tbody> +</table> + +<p>Norske tegn er definert i ISO-8859-1, som også omtales som Latin 1, de +fleste samiske tegn er definert i ISO-8859-4 (Latin 4) mens tilgang +til €-symbolet kom med ISO-8859-15 (Latin 9). ISO-8859-15 er en +revisjon av ISO-8859-1 som fjerner noen lite brukte symboler og +erstatter bokstaver som er mer brukt, og introduserer €-symbolet. Det +er viktig å merke at alle ISO-8859-variantene har overlapp med +ASCII-7, noe som ga samvirke med de engelskspråklige landene som ikke +trengte å gjøre noe. Det innebærer også at de første 128 verdiene i +ISO-8859-variantene representerer de samme symbolene. Det er først når +du kommer til tolkningen av de resterende 128 verdiene med nummer 128 +til 255, at det oppsto tolkningsutfordringer mellom +ISO-8859-variantene.</p> + +<p>ISO-8859-verdenen fungerte godt så lenge tegnsettet som ble brukt når +innhold ble skapt, også ble brukt når innhold ble gjengitt og du ikke +trengte å kombinere innhold fra forskjellige tegnsett i samme +dokument. Utfordringen med bruken av ISO-8859-variantene ble raskt +tydelig i en mer globalisert verden med utveksling av tekst på tvers +av landegrenser der tekstlig innhold i dokumenter, e-poster og +websider kunne bli skrevet med ett tegnsett og gjengitt med et annet +tegnsett.</p> + +<table align="center" width="60%"> + +<caption>Tabell 3. Viser tolkning av verdiene som er tilegnet de +norske symbolene i ISO-8859-1 i de andre ISO 8859-variatene. Merk +ISO-8859-12 ikke finnes da arbeidet ble avsluttet.<sup>[<a id="tegnsett_access_footnoteref_1" href="#tegnsett_access_footnotedef_1" title="View footnote.">1</a>]</sup></caption> + +<tbody> +<tr> +<th>Binærverdi</th> +<th>1</th> +<th>2</th> +<th>3</th> +<th>4</th> +<th>5</th> +<th>6</th> +<th>7</th> +<th>8</th> +<th>9</th> +<th>10</th> +<th>11</th> +<th>13</th> +<th>14</th> +<th>15</th> +<th>16</th> +</tr> +<tr> +<td>11000110</td> +<td>Æ</td> +<td>Ć</td> +<td>Ĉ</td> +<td>Æ</td> +<td>Ц</td> +<td>ئ</td> +<td>Ζ</td> +<td></td> +<td>Æ</td> +<td>Æ</td> +<td>ฦ</td> +<td>Ę</td> +<td>Æ</td> +<td>Æ</td> +<td>Æ</td> +</tr> +<tr> +<td>11011000</td> +<td>Ø</td> +<td>Ř</td> +<td>Ĝ</td> +<td>Ø</td> +<td>и</td> +<td>ظ</td> +<td>Ψ</td> +<td></td> +<td>Ø</td> +<td>Ø</td> +<td>ุ</td> +<td>Ų</td> +<td>Ø</td> +<td>Ø</td> +<td>Ű</td> +</tr> +<tr> +<td>11000101</td> +<td>Å</td> +<td>Ĺ</td> +<td>Ċ</td> +<td>Å</td> +<td>Х</td> +<td>إ</td> +<td>Ε</td> +<td></td> +<td>Å</td> +<td>Å</td> +<td>ล</td> +<td>Å</td> +<td>Å</td> +<td>Å</td> +<td>Ć</td> +</tr> +<tr> +<td>11100110</td> +<td>æ</td> +<td>ć</td> +<td>ĉ</td> +<td>æ</td> +<td>ц</td> +<td>ن</td> +<td>ζ</td> +<td>ז</td> +<td>æ</td> +<td>æ</td> +<td>ๆ</td> +<td>ę</td> +<td>æ</td> +<td>æ</td> +<td>v</td> +</tr> +<tr> +<td>11111000</td> +<td>ø</td> +<td>ř</td> +<td>ĝ</td> +<td>ø</td> +<td>ј</td> +<td></td> +<td>ψ</td> +<td>ר</td> +<td>ø</td> +<td>ø</td> +<td>๘</td> +<td>ų</td> +<td>ø</td> +<td>ø</td> +<td>ű</td> +</tr> +<tr> +<td>11100101</td> +<td>å</td> +<td>ĺ</td> +<td>ċ</td> +<td>å</td> +<td>х</td> +<td>م</td> +<td>ε</td> +<td>ו</td> +<td>å</td> +<td>å</td> +<td>ๅ</td> +<td>å</td> +<td>å</td> +<td>å</td> +<td>ć</td> +</tr> +</tbody> +</table> + +<p>Denne problemstillingen er illustrert i tabell 3, der vi ser verdiene +tilegnet de norske symbolene i ISO-8859-1 i kolonne «1». I de øvrige +kolonnene ser vi hvilket symbol verdien får i de andre +ISO-8859-variantene. Tar vi utgangspunkt i tabell 3, kan vi se at +ordet lærlingspørsmål gjengitt med ISO-8859-2 (kolonne 2) blir +lćrlingspřrsmĺl, mens det blir lζrlingspψrsmεl med ISO- 8859-7 +(kolonne 7). Med ISO-8859-2 blir «æ» til «ć», «ø» til «ř» og «å» til + «ĺ». I ISO-8859-7 blir «æ» til «ζ», «ø» til «ψ», mens «å» blir «ε».</p> + +<p>Det er egentlig ingen utfordring med dette så lenge du vet hvilket +tegnsett innholdet ditt er representert med, og det ikke har skjedd +omforminger som du ikke er klar over. Det er det siste som er +problematisk, spesielt de datasystemene som har vært i bruk de siste +20 årene, som ikke har noe innebygd funksjonalitet for å forvalte +tegnsettproblematikken. Et godt eksempel på dette er +Microsoft-tegnsettet Windows-1252, som ble forvekslet som 100 % +kompatibel med ISO-8859-1, men hadde byttet ut plassene fra 127 til +159. Historisk vil det finnes en del variasjon i hvilket tegnsett som +har vært i bruk, og hvor vellykket konvertering mellom tegnsett har +vært.</p> + +<p id="tegnsett_access_unicode_som_løsning"><strong>Unicode som løsning</strong></p> + +<p>Tegnsettforvirring ble etter hvert et irritasjonsmoment og +samvirkeproblem. Ofte fikk man en e-post der æøå var erstattet av rare +symboler fordi e-posten hadde vært innom et eller annet datasystem som +ikke brukte samme tegnsett.</p> + +<p>For å løse dette samvirkeproblemet for tegnsett ble det startet et +arbeid og en ny standard så dagens lys etter hvert. Denne standarden +fikk navnet Unicode (ISO/ IEC 10646) og skulle resultere i et tegnsett +som alle skulle være enige om. Unicode er et repertoar og en +representasjon, dvs. navngivning og tilordning av tallverdi til alle +symboler i bruk i verden i dag. Oppføringer i Unicode skrives gjerne +U+XXXX der XXXX er tallkoden i sekstentallssystemet som oppføringen +har i Unicode-katalogen. Her finner vi tegn brukt av både levende og +døde språk, konstruerte språk, tekniske symboler, morsomme tegninger +(såkalte emojier) og tegn ingen vet hva betyr eller skal brukes +til. Et morsomt eksempel er i nettartikkelen: U+237C ⍼ RIGHT ANGLE +WITH DOWNWARDS ZIGZAG ARROW, av Jonathan Chan.<sup>[<a id="tegnsett_access_footnoteref_2" href="#tegnsett_access_footnotedef_2" title="View footnote.">2</a>]</sup></p> + +<p>Sammen med Unicode kom det tre måter å kode disse tallene på; UTF-8, +UTF-16 og UTF-32. Av datatekniske årsaker er UTF-8 mye brukt, spesielt +når det gjelder utveksling av tekst over Internett, mens UTF-16 er +brukt en del til tekstfiler lagret på Windows. En utfordring med +Unicode og UTF-variantene er at disse gir flere måter å kode samme +symbol på med en kombinasjonsmekanisme. Dette kan gi utfordringer ved +søk, hvis en skal søke etter et ord som har ett eller flere symboler +som kan skrives på ulikt vis, så er det ikke sikkert at søkesystemet +vil finne alle forekomster. For eksempel kan bokstaven U+00F8 «Latin +Small Letter O with Stroke» kodes som den tradisjonelle norske tegnet +ø, men også som o kombinert med skråstrek U+0338. Begge deler er +gyldig bruk av Unicode, selv om det er tradisjon for å foretrekke å +«normalisere» kombinasjoner som enkelttegn der det er mulig, nettopp +for å forenkle søk.</p> + +<p id="tegnsett_access_bare_unicode_fremover"><strong>Bare Unicode fremover</strong></p> + +<p>Forvaltningens bruk av tegnsett er regulert i Forskrift om +IT-standarder i offentlig forvaltning<sup>[<a id="tegnsett_access_footnoteref_3" href="#tegnsett_access_footnotedef_3" title="View footnote.">3</a>]</sup>. Her står det: «Ved all +utveksling av informasjon mellom forvaltningsorganer og fra +forvaltningsorgan til innbyggere og næringsliv skal tegnsettstandarden +ISO/IEC 10646 representert ved UTF8 benyttes.» Det er forskjellige +bruksområder til UTF-8, UTF-16 og UTF-32, men UTF-8 er kodingen vi +kjenner mest til. Det er flere grunner at UTF-8 «vant» konkurransen +til å bli den utvalgte. Den kanskje viktigste er at UTF-8 er fullt +samvirkende med ASCII-7, slik at den engelskspråklige delen av verden +kunne rulle ut UTF-8 uten å merke noe forskjell. En tekstfil med kun +ASCII-tekst vil være identisk på disken hvis den lagres som UTF-8 og +ASCII. UTF-16 og UTF-32 byr på noen optimaliseringer som gjør dem +relevant for spesifikke problemområder, men for det meste vil vi aldri +oppleve disse standardene på nært hold i hverdagen. Det er uansett kun +bruken av UTF-8 som er lovregulert i Norge.</p> + +<p>Det er ikke slik at hele verden bruker ISO/IEC 10646 og UTF-8. Kina +har egne standarder for tegnsett, mye brukt er GB 18030, som er +Unicode med en annen koding enn UTF-8, mens Taiwan og andre asiatiske +land gjerne bruker Big5 eller andre tegnsett.</p> + +<p>UTF-8 er dominerende i Norge, men det er tidsperioder der forskjellige +datasystemer utvekslet data i henhold til ISO-8859-1, ISO-8859-15, +Windows-1252, Codepage 865 og ISO-646-60 / Codepage 1016 mens +overgangen til UTF-8 pågikk. Det er ikke slik at et datasystem enkelt +kan tvinges til å bruke et tegnsett, da det er flere lag i et +datasystem som må settes opp til å bruke riktig tegnsett, og +tegnsettproblemet fort oppstår når det er et eller annet i +datasystemet som bruker feil tegnsett.</p> + +<p>Et klassisk eksempel på problemet er en utveksling av tekst mellom to +systemer der teksten i utgangspunktet er kodet i UTF-8, men går +gjennom noe som er ISO-8859-1 underveis. Dette kan vises med at ordet +«på» i et slik scenario ender opp som «pÃ¥». Det er mulig å spore +dette tilbake til verdiene symbolene er tilordnet i tegnsettene. «på» +blir til «pÃ¥» fordi «å» i UTF-8 er representert med U+C3AF, og dersom +vi ser på hva disse verdiene representerer, ser vi at +sekstentallssystemverdien C3 er 1100 0011 i totallssystemet og +symbolet med dette tallet i ISO-8859-1 er Ã.</p> + +<p>Vi ser det samme med sekstentallssystemverdien A5, som er 1010 0101 i +totallssystemet, og tilsvarende symbol i ISO-8859-1 er ¥. Slik +mojibake kan lett skje hvis «på» i utgangspunktet var representert med +UTF-8, men ble behandlet med et system som bruker ISO-8859-1. Det er +ingen automatikk i å fange opp slike ødeleggelser mens tekstlig +innhold utveksles mellom datasystemer.</p> + +<p>En utfordring for depotarkivene er at bruken av tegnsett ikke alltid +har vært regulert, og at det kan finnes flere dokumentasjonssamlinger +som er opprettet med varierende tegnsett før gjeldende forskrift +inntraff – uten at det er mulig å avlede fra filene hvilket tegnsett +som ble brukt. Et eksempel på dette er €-symbolet, som kom først etter +at ISO-8859-1 var tatt i bruk. Det kan bli en utfordring for et +depotarkiv, men så lenge det er kjent hvilket tegnsett var i bruk, så +bør det gå bra. Riksarkivarens +forskrift<sup>[<a id="tegnsett_access_footnoteref_4" href="#tegnsett_access_footnotedef_4" title="View footnote.">4</a>]</sup> +formaliserer dette ved å kreve følgende:</p> + +<blockquote> +<p>§ 5-11. Tegnsett i arkivuttrekk</p> + +<ol> + <li>Arkivuttrekk og medfølgende struktur- og innholdsbeskrivelser skal + overføres som ren tekst i ukryptert form, og benytte godkjent + tegnsett.</li> + + <li>Godkjente tegnsett er: + <ol> + <li>Unicode UTF-8<br> + (ISO/IEC 10646-1:2000 Annex D)</li> + <li>ISO 8859-1:1998, Latin 1</li> + <li>ISO 8859-4:1998, Latin 4 for samiske tegn.</li> + </ol></li> + + <li>Andre tegnsett aksepteres bare etter avtale med Arkivverket.</li> +</ol> +</blockquote> + +<p id="tegnsett_access_ditt_ansvar"><strong>Ditt ansvar</strong></p> + +<p>På mange måter burde ikke tegnsett være et problem i 2023, men sånn er +det nok ikke. Land som har oppgradert til UTF-8 som primærtegnsett for +utveksling av tekstlig innhold, begrenser problematikken betraktelig, +men globalt sett så er tegnsettutfordringen ikke løst fordi ikke alle +er enige om å bruke samme tegnsett. Det kan være geopolitiske eller +kulturelle hensyn som ligger til grunn for dette.</p> + +<p>Det er uansett verdt å merke at selv om bruken av UTF-8 skulle bli +100% utbredt, så er det et historisk perspektiv (ASCII-7, +ISO-8859-variantene, UTF-8) her som gjør tegnsett til et problemområde +arkivarene må forstå og håndtere. Som danningsarkivar har du et +ansvar for å vite hvilket tegnsett systemene og databasene dere +forvalter, er i samsvar med. Det er noe IT-avdelingen din eller +programvareleverandørene enkelt skal kunne svare på, og svaret skal +være UTF-8 for alle nye systemer.</p> + +<hr> + +<p id="tegnsett_access_footnotedef_1"><a href="#tegnsett_access_footnoteref_1">1</a>. Tegnsettkilde <a href="https://en.wikipedia.org/wiki/ISO/IEC_8859">https://en.wikipedia.org/wiki/ISO/IEC_8859</a></p> + +<p id="tegnsett_access_footnotedef_2"><a href="#tegnsett_access_footnoteref_2">2</a>. <a href="https://ionathan.ch/2022/04/09/angzarr.html">https://ionathan.ch/2022/04/09/angzarr.html</a></p> + +<p id="tegnsett_access_footnotedef_3"><a href="#tegnsett_access_footnoteref_3">3</a>. <a href="https://lovdata.no/dokument/SF/forskrift/2013-04-05-959/%C2%A78#%C2%A78">https://lovdata.no/dokument/SF/forskrift/2013-04-05-959/%C2%A78#%C2%A78</a></p> + +<p id="tegnsett_access_footnotedef_4"><a href="#tegnsett_access_footnoteref_4">4</a>. <a href="https://lovdata.no/forskrift/2017-12-19-2286/§5-11">https://lovdata.no/forskrift/2017-12-19-2286/§5-11</a></p> + +</blockquote> + +<p>For øvrig burde varsleren Edward Snowden få politisk asyl i Norge.</p> + +<p>Som vanlig, hvis du bruker Bitcoin og ønsker å vise din støtte til +det jeg driver med, setter jeg pris på om du sender Bitcoin-donasjoner +til min adresse +<b><a href="bitcoin:15oWEoG9dUPovwmUL9KWAnYRtNJEkP1u1b">15oWEoG9dUPovwmUL9KWAnYRtNJEkP1u1b</a></b>. Merk, +betaling med bitcoin er ikke anonymt. :)</p> + +<p><b>Oppdatering 2024-08-23</b>: Har fått innspill om at det norske +ordet for japanske mojibake er tegnsalat.</p> + + + + + New and improved sqlcipher in Debian for accessing Signal database + http://www.hungry.com/~pere/blog/New_and_improved_sqlcipher_in_Debian_for_accessing_Signal_database.html + http://www.hungry.com/~pere/blog/New_and_improved_sqlcipher_in_Debian_for_accessing_Signal_database.html + Sun, 12 Nov 2023 12:00:00 +0100 + <p>For a while now I wanted to have direct access to the +<a href="https://signal.org/">Signal</a> database of messages and +channels of my Desktop edition of Signal. I prefer the enforced end +to end encryption of Signal these days for my communication with +friends and family, to increase the level of safety and privacy as +well as raising the cost of the mass surveillance government and +non-government entities practice these days. In August I came across +a nice +<a href="https://www.yoranbrondsema.com/post/the-guide-to-extracting-statistics-from-your-signal-conversations/">recipe +on how to use sqlcipher to extract statistics from the Signal +database</a> explaining how to do this. Unfortunately this did not +work with the version of sqlcipher in Debian. The +<a href="http://tracker.debian.org/sqlcipher/">sqlcipher</a> +package is a "fork" of the sqlite package with added support for +encrypted databases. Sadly the current Debian maintainer +<a href="https://bugs.debian.org/961598">announced more than three +years ago that he did not have time to maintain sqlcipher</a>, so it +seemed unlikely to be upgraded by the maintainer. I was reluctant to +take on the job myself, as I have very limited experience maintaining +shared libraries in Debian. After waiting and hoping for a few +months, I gave up the last week, and set out to update the package. In +the process I orphaned it to make it more obvious for the next person +looking at it that the package need proper maintenance.</p> + +<p>The version in Debian was around five years old, and quite a lot of +changes had taken place upstream into the Debian maintenance git +repository. After spending a few days importing the new upstream +versions, realising that upstream did not care much for SONAME +versioning as I saw library symbols being both added and removed with +minor version number changes to the project, I concluded that I had to +do a SONAME bump of the library package to avoid surprising the +reverse dependencies. I even added a simple +autopkgtest script to ensure the package work as intended. Dug deep +into the hole of learning shared library maintenance, I set out a few +days ago to upload the new version to Debian experimental to see what +the quality assurance framework in Debian had to say about the result. +The feedback told me the pacakge was not too shabby, and yesterday I +uploaded the latest version to Debian unstable. It should enter +testing today or tomorrow, perhaps delayed by +<a href="https://bugs.debian.org/1055812">a small library +transition</a>.</p> + +<p>Armed with a new version of sqlcipher, I can now have a look at the +SQL database in ~/.config/Signal/sql/db.sqlite. First, one need to +fetch the encryption key from the Signal configuration using this +simple JSON extraction command:</p> + +<pre>/usr/bin/jq -r '."key"' ~/.config/Signal/config.json</pre> + +<p>Assuming the result from that command is 'secretkey', which is a +hexadecimal number representing the key used to encrypt the database. +Next, one can now connect to the database and inject the encryption +key for access via SQL to fetch information from the database. Here +is an example dumping the database structure:</p> + +<pre> +% sqlcipher ~/.config/Signal/sql/db.sqlite +sqlite> PRAGMA key = "x'secretkey'"; +sqlite> .schema +CREATE TABLE sqlite_stat1(tbl,idx,stat); +CREATE TABLE conversations( + id STRING PRIMARY KEY ASC, + json TEXT, + + active_at INTEGER, + type STRING, + members TEXT, + name TEXT, + profileName TEXT + , profileFamilyName TEXT, profileFullName TEXT, e164 TEXT, serviceId TEXT, groupId TEXT, profileLastFetchedAt INTEGER); +CREATE TABLE identityKeys( + id STRING PRIMARY KEY ASC, + json TEXT + ); +CREATE TABLE items( + id STRING PRIMARY KEY ASC, + json TEXT + ); +CREATE TABLE sessions( + id TEXT PRIMARY KEY, + conversationId TEXT, + json TEXT + , ourServiceId STRING, serviceId STRING); +CREATE TABLE attachment_downloads( + id STRING primary key, + timestamp INTEGER, + pending INTEGER, + json TEXT + ); +CREATE TABLE sticker_packs( + id TEXT PRIMARY KEY, + key TEXT NOT NULL, + + author STRING, + coverStickerId INTEGER, + createdAt INTEGER, + downloadAttempts INTEGER, + installedAt INTEGER, + lastUsed INTEGER, + status STRING, + stickerCount INTEGER, + title STRING + , attemptedStatus STRING, position INTEGER DEFAULT 0 NOT NULL, storageID STRING, storageVersion INTEGER, storageUnknownFields BLOB, storageNeedsSync + INTEGER DEFAULT 0 NOT NULL); +CREATE TABLE stickers( + id INTEGER NOT NULL, + packId TEXT NOT NULL, + + emoji STRING, + height INTEGER, + isCoverOnly INTEGER, + lastUsed INTEGER, + path STRING, + width INTEGER, + + PRIMARY KEY (id, packId), + CONSTRAINT stickers_fk + FOREIGN KEY (packId) + REFERENCES sticker_packs(id) + ON DELETE CASCADE + ); +CREATE TABLE sticker_references( + messageId STRING, + packId TEXT, + CONSTRAINT sticker_references_fk + FOREIGN KEY(packId) + REFERENCES sticker_packs(id) + ON DELETE CASCADE + ); +CREATE TABLE emojis( + shortName TEXT PRIMARY KEY, + lastUsage INTEGER + ); +CREATE TABLE messages( + rowid INTEGER PRIMARY KEY ASC, + id STRING UNIQUE, + json TEXT, + readStatus INTEGER, + expires_at INTEGER, + sent_at INTEGER, + schemaVersion INTEGER, + conversationId STRING, + received_at INTEGER, + source STRING, + hasAttachments INTEGER, + hasFileAttachments INTEGER, + hasVisualMediaAttachments INTEGER, + expireTimer INTEGER, + expirationStartTimestamp INTEGER, + type STRING, + body TEXT, + messageTimer INTEGER, + messageTimerStart INTEGER, + messageTimerExpiresAt INTEGER, + isErased INTEGER, + isViewOnce INTEGER, + sourceServiceId TEXT, serverGuid STRING NULL, sourceDevice INTEGER, storyId STRING, isStory INTEGER + GENERATED ALWAYS AS (type IS 'story'), isChangeCreatedByUs INTEGER NOT NULL DEFAULT 0, isTimerChangeFromSync INTEGER + GENERATED ALWAYS AS ( + json_extract(json, '$.expirationTimerUpdate.fromSync') IS 1 + ), seenStatus NUMBER default 0, storyDistributionListId STRING, expiresAt INT + GENERATED ALWAYS + AS (ifnull( + expirationStartTimestamp + (expireTimer * 1000), + 9007199254740991 + )), shouldAffectActivity INTEGER + GENERATED ALWAYS AS ( + type IS NULL + OR + type NOT IN ( + 'change-number-notification', + 'contact-removed-notification', + 'conversation-merge', + 'group-v1-migration', + 'keychange', + 'message-history-unsynced', + 'profile-change', + 'story', + 'universal-timer-notification', + 'verified-change' + ) + ), shouldAffectPreview INTEGER + GENERATED ALWAYS AS ( + type IS NULL + OR + type NOT IN ( + 'change-number-notification', + 'contact-removed-notification', + 'conversation-merge', + 'group-v1-migration', + 'keychange', + 'message-history-unsynced', + 'profile-change', + 'story', + 'universal-timer-notification', + 'verified-change' + ) + ), isUserInitiatedMessage INTEGER + GENERATED ALWAYS AS ( + type IS NULL + OR + type NOT IN ( + 'change-number-notification', + 'contact-removed-notification', + 'conversation-merge', + 'group-v1-migration', + 'group-v2-change', + 'keychange', + 'message-history-unsynced', + 'profile-change', + 'story', + 'universal-timer-notification', + 'verified-change' + ) + ), mentionsMe INTEGER NOT NULL DEFAULT 0, isGroupLeaveEvent INTEGER + GENERATED ALWAYS AS ( + type IS 'group-v2-change' AND + json_array_length(json_extract(json, '$.groupV2Change.details')) IS 1 AND + json_extract(json, '$.groupV2Change.details[0].type') IS 'member-remove' AND + json_extract(json, '$.groupV2Change.from') IS NOT NULL AND + json_extract(json, '$.groupV2Change.from') IS json_extract(json, '$.groupV2Change.details[0].aci') + ), isGroupLeaveEventFromOther INTEGER + GENERATED ALWAYS AS ( + isGroupLeaveEvent IS 1 + AND + isChangeCreatedByUs IS 0 + ), callId TEXT + GENERATED ALWAYS AS ( + json_extract(json, '$.callId') + )); +CREATE TABLE sqlite_stat4(tbl,idx,neq,nlt,ndlt,sample); +CREATE TABLE jobs( + id TEXT PRIMARY KEY, + queueType TEXT STRING NOT NULL, + timestamp INTEGER NOT NULL, + data STRING TEXT + ); +CREATE TABLE reactions( + conversationId STRING, + emoji STRING, + fromId STRING, + messageReceivedAt INTEGER, + targetAuthorAci STRING, + targetTimestamp INTEGER, + unread INTEGER + , messageId STRING); +CREATE TABLE senderKeys( + id TEXT PRIMARY KEY NOT NULL, + senderId TEXT NOT NULL, + distributionId TEXT NOT NULL, + data BLOB NOT NULL, + lastUpdatedDate NUMBER NOT NULL + ); +CREATE TABLE unprocessed( + id STRING PRIMARY KEY ASC, + timestamp INTEGER, + version INTEGER, + attempts INTEGER, + envelope TEXT, + decrypted TEXT, + source TEXT, + serverTimestamp INTEGER, + sourceServiceId STRING + , serverGuid STRING NULL, sourceDevice INTEGER, receivedAtCounter INTEGER, urgent INTEGER, story INTEGER); +CREATE TABLE sendLogPayloads( + id INTEGER PRIMARY KEY ASC, + + timestamp INTEGER NOT NULL, + contentHint INTEGER NOT NULL, + proto BLOB NOT NULL + , urgent INTEGER, hasPniSignatureMessage INTEGER DEFAULT 0 NOT NULL); +CREATE TABLE sendLogRecipients( + payloadId INTEGER NOT NULL, + + recipientServiceId STRING NOT NULL, + deviceId INTEGER NOT NULL, + + PRIMARY KEY (payloadId, recipientServiceId, deviceId), + + CONSTRAINT sendLogRecipientsForeignKey + FOREIGN KEY (payloadId) + REFERENCES sendLogPayloads(id) + ON DELETE CASCADE + ); +CREATE TABLE sendLogMessageIds( + payloadId INTEGER NOT NULL, + + messageId STRING NOT NULL, + + PRIMARY KEY (payloadId, messageId), + + CONSTRAINT sendLogMessageIdsForeignKey + FOREIGN KEY (payloadId) + REFERENCES sendLogPayloads(id) + ON DELETE CASCADE + ); +CREATE TABLE preKeys( + id STRING PRIMARY KEY ASC, + json TEXT + , ourServiceId NUMBER + GENERATED ALWAYS AS (json_extract(json, '$.ourServiceId'))); +CREATE TABLE signedPreKeys( + id STRING PRIMARY KEY ASC, + json TEXT + , ourServiceId NUMBER + GENERATED ALWAYS AS (json_extract(json, '$.ourServiceId'))); +CREATE TABLE badges( + id TEXT PRIMARY KEY, + category TEXT NOT NULL, + name TEXT NOT NULL, + descriptionTemplate TEXT NOT NULL + ); +CREATE TABLE badgeImageFiles( + badgeId TEXT REFERENCES badges(id) + ON DELETE CASCADE + ON UPDATE CASCADE, + 'order' INTEGER NOT NULL, + url TEXT NOT NULL, + localPath TEXT, + theme TEXT NOT NULL + ); +CREATE TABLE storyReads ( + authorId STRING NOT NULL, + conversationId STRING NOT NULL, + storyId STRING NOT NULL, + storyReadDate NUMBER NOT NULL, + + PRIMARY KEY (authorId, storyId) + ); +CREATE TABLE storyDistributions( + id STRING PRIMARY KEY NOT NULL, + name TEXT, + + senderKeyInfoJson STRING + , deletedAtTimestamp INTEGER, allowsReplies INTEGER, isBlockList INTEGER, storageID STRING, storageVersion INTEGER, storageUnknownFields BLOB, storageNeedsSync INTEGER); +CREATE TABLE storyDistributionMembers( + listId STRING NOT NULL REFERENCES storyDistributions(id) + ON DELETE CASCADE + ON UPDATE CASCADE, + serviceId STRING NOT NULL, + + PRIMARY KEY (listId, serviceId) + ); +CREATE TABLE uninstalled_sticker_packs ( + id STRING NOT NULL PRIMARY KEY, + uninstalledAt NUMBER NOT NULL, + storageID STRING, + storageVersion NUMBER, + storageUnknownFields BLOB, + storageNeedsSync INTEGER NOT NULL + ); +CREATE TABLE groupCallRingCancellations( + ringId INTEGER PRIMARY KEY, + createdAt INTEGER NOT NULL + ); +CREATE TABLE IF NOT EXISTS 'messages_fts_data'(id INTEGER PRIMARY KEY, block BLOB); +CREATE TABLE IF NOT EXISTS 'messages_fts_idx'(segid, term, pgno, PRIMARY KEY(segid, term)) WITHOUT ROWID; +CREATE TABLE IF NOT EXISTS 'messages_fts_content'(id INTEGER PRIMARY KEY, c0); +CREATE TABLE IF NOT EXISTS 'messages_fts_docsize'(id INTEGER PRIMARY KEY, sz BLOB); +CREATE TABLE IF NOT EXISTS 'messages_fts_config'(k PRIMARY KEY, v) WITHOUT ROWID; +CREATE TABLE edited_messages( + messageId STRING REFERENCES messages(id) + ON DELETE CASCADE, + sentAt INTEGER, + readStatus INTEGER + , conversationId STRING); +CREATE TABLE mentions ( + messageId REFERENCES messages(id) ON DELETE CASCADE, + mentionAci STRING, + start INTEGER, + length INTEGER + ); +CREATE TABLE kyberPreKeys( + id STRING PRIMARY KEY NOT NULL, + json TEXT NOT NULL, ourServiceId NUMBER + GENERATED ALWAYS AS (json_extract(json, '$.ourServiceId'))); +CREATE TABLE callsHistory ( + callId TEXT PRIMARY KEY, + peerId TEXT NOT NULL, -- conversation id (legacy) | uuid | groupId | roomId + ringerId TEXT DEFAULT NULL, -- ringer uuid + mode TEXT NOT NULL, -- enum "Direct" | "Group" + type TEXT NOT NULL, -- enum "Audio" | "Video" | "Group" + direction TEXT NOT NULL, -- enum "Incoming" | "Outgoing + -- Direct: enum "Pending" | "Missed" | "Accepted" | "Deleted" + -- Group: enum "GenericGroupCall" | "OutgoingRing" | "Ringing" | "Joined" | "Missed" | "Declined" | "Accepted" | "Deleted" + status TEXT NOT NULL, + timestamp INTEGER NOT NULL, + UNIQUE (callId, peerId) ON CONFLICT FAIL + ); +[ dropped all indexes to save space in this blog post ] +CREATE TRIGGER messages_on_view_once_update AFTER UPDATE ON messages + WHEN + new.body IS NOT NULL AND new.isViewOnce = 1 + BEGIN + DELETE FROM messages_fts WHERE rowid = old.rowid; + END; +CREATE TRIGGER messages_on_insert AFTER INSERT ON messages + WHEN new.isViewOnce IS NOT 1 AND new.storyId IS NULL + BEGIN + INSERT INTO messages_fts + (rowid, body) + VALUES + (new.rowid, new.body); + END; +CREATE TRIGGER messages_on_delete AFTER DELETE ON messages BEGIN + DELETE FROM messages_fts WHERE rowid = old.rowid; + DELETE FROM sendLogPayloads WHERE id IN ( + SELECT payloadId FROM sendLogMessageIds + WHERE messageId = old.id + ); + DELETE FROM reactions WHERE rowid IN ( + SELECT rowid FROM reactions + WHERE messageId = old.id + ); + DELETE FROM storyReads WHERE storyId = old.storyId; + END; +CREATE VIRTUAL TABLE messages_fts USING fts5( + body, + tokenize = 'signal_tokenizer' + ); +CREATE TRIGGER messages_on_update AFTER UPDATE ON messages + WHEN + (new.body IS NULL OR old.body IS NOT new.body) AND + new.isViewOnce IS NOT 1 AND new.storyId IS NULL + BEGIN + DELETE FROM messages_fts WHERE rowid = old.rowid; + INSERT INTO messages_fts + (rowid, body) + VALUES + (new.rowid, new.body); + END; +CREATE TRIGGER messages_on_insert_insert_mentions AFTER INSERT ON messages + BEGIN + INSERT INTO mentions (messageId, mentionAci, start, length) + + SELECT messages.id, bodyRanges.value ->> 'mentionAci' as mentionAci, + bodyRanges.value ->> 'start' as start, + bodyRanges.value ->> 'length' as length + FROM messages, json_each(messages.json ->> 'bodyRanges') as bodyRanges + WHERE bodyRanges.value ->> 'mentionAci' IS NOT NULL + + AND messages.id = new.id; + END; +CREATE TRIGGER messages_on_update_update_mentions AFTER UPDATE ON messages + BEGIN + DELETE FROM mentions WHERE messageId = new.id; + INSERT INTO mentions (messageId, mentionAci, start, length) + + SELECT messages.id, bodyRanges.value ->> 'mentionAci' as mentionAci, + bodyRanges.value ->> 'start' as start, + bodyRanges.value ->> 'length' as length + FROM messages, json_each(messages.json ->> 'bodyRanges') as bodyRanges + WHERE bodyRanges.value ->> 'mentionAci' IS NOT NULL + + AND messages.id = new.id; + END; +sqlite> +</pre> + +<p>Finally I have the tool needed to inspect and process Signal +messages that I need, without using the vendor provided client. Now +on to transforming it to a more useful format.</p> + +<p>As usual, if you use Bitcoin and want to show your support of my +activities, please send Bitcoin donations to my address +<b><a href="bitcoin:15oWEoG9dUPovwmUL9KWAnYRtNJEkP1u1b">15oWEoG9dUPovwmUL9KWAnYRtNJEkP1u1b</a></b>.</p> + + + New chrpath release 0.17 - https://people.skolelinux.org/pere/blog/New_chrpath_release_0_17.html - https://people.skolelinux.org/pere/blog/New_chrpath_release_0_17.html + http://www.hungry.com/~pere/blog/New_chrpath_release_0_17.html + http://www.hungry.com/~pere/blog/New_chrpath_release_0_17.html Fri, 10 Nov 2023 07:30:00 +0100 <p>The chrpath package provide a simple command line tool to remove or modify the rpath or runpath of compiled ELF program. It is almost 10 @@ -44,8 +1081,8 @@ activities, please send Bitcoin donations to my address Test framework for DocBook processors / formatters - https://people.skolelinux.org/pere/blog/Test_framework_for_DocBook_processors___formatters.html - https://people.skolelinux.org/pere/blog/Test_framework_for_DocBook_processors___formatters.html + http://www.hungry.com/~pere/blog/Test_framework_for_DocBook_processors___formatters.html + http://www.hungry.com/~pere/blog/Test_framework_for_DocBook_processors___formatters.html Sun, 5 Nov 2023 13:00:00 +0100 <p>All the books I have published so far has been using <a href="https://docbook.org/">DocBook</a> somewhere in the process. @@ -132,7 +1169,7 @@ having a look at the <a href="https://docbook.org/">the DocBook web site</a>, <a href="https://doccookbook.sourceforge.net/html/en/">the DoCookBook site<a/> and my earlier blog post on -<a href="https://people.skolelinux.org/pere/blog/From_English_wiki_to_translated_PDF_and_epub_via_Docbook.html">how +<a href="http://www.hungry.com/~pere/blog/From_English_wiki_to_translated_PDF_and_epub_via_Docbook.html">how the Skolelinux project process and translate documentation</a>, a talk I gave earlier this year on <a href="https://www.nuug.no/aktiviteter/20230314-oversetting-og-publisering-av-b%c3%b8ker-med-fri-programvare/">how to translate and publish books using free software</a> (Norwegian