From ec9cd8b2496ffc00b8cddc1bac57f5dba5f70e8d Mon Sep 17 00:00:00 2001 From: rald Date: Fri, 4 Aug 2023 22:14:10 +0800 Subject: [PATCH] OK --- README.md | 19 +++ add-cert.cgi | 26 ++++ boggle.py => boggle.cgi | 32 +++-- boggle.sqlite3 | Bin 0 -> 49152 bytes change-name.cgi | 24 ++++ check.cgi | 15 +++ choose.cgi | 18 +++ create_schema.sql | 34 +++++ db.py | 279 ++++++++++++++++++++++++++++++++++++++++ game.cgi | 112 ++++++++++++++++ guess.cgi | 13 ++ helpers.py | 40 ++++++ index.cgi | 16 +++ lobby.cgi | 28 ++++ login.cgi | 36 ++++++ new_game.cgi | 105 +++++++++++++++ update-auth-code.cgi | 28 ++++ 17 files changed, 814 insertions(+), 11 deletions(-) create mode 100644 README.md create mode 100755 add-cert.cgi rename boggle.py => boggle.cgi (84%) create mode 100644 boggle.sqlite3 create mode 100755 change-name.cgi create mode 100755 check.cgi create mode 100755 choose.cgi create mode 100644 create_schema.sql create mode 100644 db.py create mode 100755 game.cgi create mode 100755 guess.cgi create mode 100644 helpers.py create mode 100755 index.cgi create mode 100755 lobby.cgi create mode 100755 login.cgi create mode 100755 new_game.cgi create mode 100755 update-auth-code.cgi diff --git a/README.md b/README.md new file mode 100644 index 0000000..9bc52a9 --- /dev/null +++ b/README.md @@ -0,0 +1,19 @@ +# CGI Example Project + +The goal of this project is to give a working example of a basic Gemini CGI application. User identities are driven by TLS Client Certificates. This project will serve as a reference for CGI authors and be explained in detail in an instructional video. + +## Features + +* (DONE) New TLS certificates automatically create a new user account +* (DONE) Users can set data on their own account (a name field for example) +* (DONE) Users can add additional certificates to the same account by using a special code + +## Setup + +Create database with: + +``` sqlite command to create database +sqlite3 db/cgi-example.sqlite3 < create_schema.sql +``` + +Then give write permission to the both the database and the containing directory (`db`) to the user that your Gemini server runs as. It is important that the containing folder write permissions are in place or you will see `50` errors returned when trying to write to the database. diff --git a/add-cert.cgi b/add-cert.cgi new file mode 100755 index 0000000..50287cf --- /dev/null +++ b/add-cert.cgi @@ -0,0 +1,26 @@ +#!/usr/bin/env python3 + +# modules are all in the parent directory +import sys +sys.path.append('..') + +# import helpers from modules +from helpers import get_client_cert, get_query_string +from db import add_cert_to_user + +tls_client_hash = get_client_cert(False) +key_code = get_query_string("What is your auth code?") +success = add_cert_to_user(key_code, tls_client_hash) + +print("# Add Cert Page") +print() + +if success: + print("Your new key has been added to this account. Congratulations!") +else: + print("Either your auth code is incorrect or something else has gone wrong.") +print() +print("=> login.cgi Back to main page") + + +#vim:fenc=utf-8:ts=4:sw=4:sta:noet:sts=4:fdm=marker:ai diff --git a/boggle.py b/boggle.cgi similarity index 84% rename from boggle.py rename to boggle.cgi index f550d6e..6726420 100755 --- a/boggle.py +++ b/boggle.cgi @@ -1,9 +1,9 @@ #!/usr/bin/env python3 - - import random +from helpers import get_query_string, get_client_cert,show_header_ok + class Trie: @@ -21,14 +21,16 @@ class Trie: curr.next[k]=Trie(j) curr=curr.next[k] curr.mark=True + + - - -f=open('dice.txt','r') +f=open('cgi-bin/dice.txt','r') dice=f.read().split('\n') f.close() dice=list(filter(None,dice)) + + random.shuffle(dice) b=[] for j in range(4): @@ -37,16 +39,12 @@ for j in range(4): die=dice[j*4+i] b[j].append(die[random.randrange(0,len(die))].upper()) -for j in range(4): - for i in range(4): - print(b[j][i],end=' ') - print() def r(s): return s and len(s)>=3 and len(s)<=16 -f=open('enable.txt','r') +f=open('cgi-bin/enable.txt','r') words=f.read().split('\n') f.close() words=list(filter(r,words)) @@ -88,6 +86,18 @@ for j in range(4): dfs(i,j,0,trie) found.sort(key=len,reverse=True) -print(found) + +show_header_ok() +print("```") +for j in range(4): + for i in range(4): + print(b[j][i],end=' ') + print() +print() +print("```") +# for z in found: print(z,end=' ') +print() +print("=> guess.cgi Guess a word") + diff --git a/boggle.sqlite3 b/boggle.sqlite3 new file mode 100644 index 0000000000000000000000000000000000000000..e547a38b3122462c9e85f25c9e9dab82ea17f966 GIT binary patch literal 49152 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 login.cgi Back to main page") + + +#vim:fenc=utf-8:ts=4:sw=4:sta:noet:sts=4:fdm=marker:ai diff --git a/check.cgi b/check.cgi new file mode 100755 index 0000000..a8228ef --- /dev/null +++ b/check.cgi @@ -0,0 +1,15 @@ +#!/usr/bin/env python3 + +import os +import random + +from helpers import get_query_string, get_client_cert,show_header_ok,show_query_string_required + +QUERY_STRING=os.getenv("QUERY_STRING") +if not QUERY_STRING: + print("30 guess.cgi",end='\r\n') + +show_header_ok() + +print(QUERY_STRING) + diff --git a/choose.cgi b/choose.cgi new file mode 100755 index 0000000..3e46fbf --- /dev/null +++ b/choose.cgi @@ -0,0 +1,18 @@ +#!/usr/bin/env python3 + +import random + +from helpers import get_client_cert,show_redirect +from db import * +from urllib import parse + +TLS_CLIENT_HASH = get_client_cert(False) +user_id = check_hash(TLS_CLIENT_HASH) +user_name = get_name(user_id) or "[Not Set]" + +url = os.getenv("GEMINI_URL") +game_id=parse.parse_qs(parse.urlparse(url).query)['game_id'][0] + +set_current_game(user_id,game_id) + +show_redirect("game.cgi") diff --git a/create_schema.sql b/create_schema.sql new file mode 100644 index 0000000..49ed9f6 --- /dev/null +++ b/create_schema.sql @@ -0,0 +1,34 @@ +BEGIN TRANSACTION; +CREATE TABLE IF NOT EXISTS "certs" ( + "hash" TEXT NOT NULL UNIQUE, + "user_id" INTEGER, + PRIMARY KEY("hash"), + FOREIGN KEY("user_id") REFERENCES "users"("id") +); +CREATE TABLE IF NOT EXISTS "users" ( + "id" INTEGER NOT NULL UNIQUE, + "name" TEXT, + "add_key_code" TEXT, + "game_id" INTEGER, + "score" INTEGER DEFAULT 0, + PRIMARY KEY("id" AUTOINCREMENT), + FOREIGN KEY("game_id") REFERENCES "games"("id") +); +CREATE TABLE IF NOT EXISTS "games" ( + "id" INTEGER NOT NULL UNIQUE, + "board" TEXT, + "words" TEXT, + "graph" TEXT, + "stats" TEXT, + "stime" DATETIME DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY("id" AUTOINCREMENT) +); +CREATE TABLE IF NOT EXISTS "plays" ( + "game_id" INTEGER, + "user_id" INTEGER, + "words" TEXT, + PRIMARY KEY("game_id","user_id"), + FOREIGN KEY("game_id") REFERENCES "games"("id"), + FOREIGN KEY("user_id") REFERENCES "users"("id") +); +COMMIT; diff --git a/db.py b/db.py new file mode 100644 index 0000000..73bcf11 --- /dev/null +++ b/db.py @@ -0,0 +1,279 @@ +import sqlite3 +from sqlite3 import Error +import os +import urllib.parse + +dir_name = os.path.dirname(__file__) +db_name = r"boggle.sqlite3" +db_file = os.path.join(dir_name, db_name) + +def create_connection(): + """ create a database connection to the SQLite database + specified by db_file + :return: Connection object or None + """ + conn = None + try: + conn = sqlite3.connect(db_file) + except Error as e: + print(e) + return conn + +def add_user(conn): + """ + Create a new user record + :param conn: database connection object + :return: user id + """ + sql = ''' INSERT INTO users DEFAULT VALUES ''' + cur = conn.cursor() + try: + cur.execute(sql) + conn.commit() + except: + return 'error in insert' + return cur.lastrowid + +def add_hash(conn, tls_client_hash, user_id): + """ + Create a new record for this cert + :param conn: database connection object + :param tls_client_hash: + :param user_id: + :return: certificate id + """ + sql = ''' INSERT OR REPLACE INTO certs(hash,user_id) VALUES(?,?) ''' + cur = conn.cursor() + cur.execute(sql, (tls_client_hash, user_id)) + conn.commit() + return cur.lastrowid + +def get_user(conn, tls_client_hash): + """ + Get user id matching tls_client_hash if it exists + :param conn: database connection object + :param tls_client_hash: + :return: user id or None + """ + cur = conn.cursor() + cur.execute("SELECT user_id FROM certs WHERE hash=?", (tls_client_hash,)) + row = cur.fetchone() + if row is None: + return None + else: + return row[0] # user_id + +def get_user_by_keycode(conn, key_code): + """ + Get user id of tls_client_hash if it exists + :param conn: database connection object + :param key_code: authorization key code + :return: user id or None + """ + cur = conn.cursor() + cur.execute("SELECT id FROM users WHERE add_key_code=?", (key_code,)) + row = cur.fetchone() + if row is None: + return None + else: + return row[0] # user_id + +def check_hash(tls_client_hash): + """ + Check for existing user with hash or add a new one + :param conn: + :param tls_client_hash: + :return: user id + """ + conn = create_connection() + with conn: + user_id = get_user(conn, tls_client_hash) + if (user_id is None): + user_id = add_user(conn) + add_hash(conn, tls_client_hash, user_id) + return user_id + +def get_name(user_id): + """ + Get user name if it exists + :param user_id: + :return: user name + """ + conn = create_connection() + with conn: + cur = conn.cursor() + cur.execute("SELECT name FROM users WHERE id=?", (user_id,)) + row = cur.fetchone() + if row is None: + return None + else: + return row[0] # user_name + +def set_name(user_id, user_name): + """ + Update or set name on user + :param user_id: + :param user_name: + """ + conn = create_connection() + with conn: + sql = ''' UPDATE users SET name=(?) WHERE id=? ''' + cur = conn.cursor() + cur.execute(sql, (urllib.parse.unquote(user_name), user_id)) + conn.commit() + return cur.lastrowid + + + +def set_add_key_code(user_id, key_code): + """ + Update or set key code on user + :param user_id: + :param key_code: + """ + conn = create_connection() + with conn: + """ There should probably be a check here to ensure no user + has this key_code already + """ + sql = ''' UPDATE users SET add_key_code=(?) WHERE id=? ''' + cur = conn.cursor() + cur.execute(sql, (key_code, user_id)) + conn.commit() + return cur.lastrowid + +def add_cert_to_user(key_code, tls_client_hash): + """ + Check for existing user with hash or add a new one + :param conn: + :param key_code: + :param tls_client_hash: + :return: true if added, false if not found + """ + conn = create_connection() + with conn: + user_id = get_user_by_keycode(conn, key_code) + if (user_id is None): + return False + add_hash(conn, tls_client_hash, user_id) + return True + + + +def create_new_game(conn,board,words,graph): + """ + Create a new game record + :param conn: database connection object + :param board: game board + :param words: found words + :param graph: word guessed list + :return: game id + """ + sql = ''' INSERT INTO games(board,words,graph,stats) VALUES(?,?,?,?) ''' + cur = conn.cursor() + cur.execute(sql, + (board,words,graph,"PLAY") + ) + conn.commit() + return cur.lastrowid + +def set_current_game(user_id,game_id): + conn = create_connection() + with conn: + cur = conn.cursor() + cur.execute("UPDATE users SET game_id=? WHERE id=?", (game_id,user_id)) + + +def get_game_id(user_id): + """ + Get game id if it exists + :param user_id:. + :return: game id + """ + conn = create_connection() + with conn: + cur = conn.cursor() + cur.execute("SELECT game_id FROM users WHERE id=?", (user_id,)) + row = cur.fetchone() + if row is None: + return None + else: + return row[0] # game_id + +def get_play_words(game_id,user_id): + """ + Get game id if it exists + :param user_id:. + :return: game id + """ + conn = create_connection() + with conn: + cur = conn.cursor() + cur.execute("SELECT words FROM plays WHERE game_id=? and user_id=?", (game_id,user_id,)) + row = cur.fetchone() + if row is None: + return None + else: + return row[0] # game_id + +def add_plays(game_id,user_id,words): + conn = create_connection() + with conn: + cur = conn.cursor() + cur.execute("INSERT OR REPLACE INTO plays(game_id,user_id,words) values(?,?,?)", (game_id,user_id,words)) + +def set_game_graph(game_id,graph): + conn = create_connection() + with conn: + cur = conn.cursor() + cur.execute("UPDATE games SET graph=? WHERE id=?", (graph,game_id)) + +def get_game_graph(game_id): + conn = create_connection() + with conn: + cur = conn.cursor() + cur.execute("SELECT graph FROM games WHERE id=?", (game_id,)) + row = cur.fetchone() + if row is None: + return None + else: + return row[0] # game_id + + +def set_game_stats(game_id,stats): + conn = create_connection() + with conn: + cur = conn.cursor() + cur.execute("UPDATE games SET stats=? WHERE id=?", (stats,game_id)) + +def get_game_stats(game_id): + conn = create_connection() + with conn: + cur = conn.cursor() + cur.execute("SELECT stats FROM games WHERE id=?", (game_id,)) + row = cur.fetchone() + if row is None: + return None + else: + return row[0] # game_id + +def add_score(user_id,points): + conn = create_connection() + with conn: + cur = conn.cursor() + cur.execute("UPDATE users SET score=score+? WHERE id=?", (points,user_id)) + +def get_score(user_id): + conn = create_connection() + with conn: + cur = conn.cursor() + cur.execute("SELECT score FROM users WHERE id=?", (user_id,)) + row = cur.fetchone() + if row is None: + return None + else: + return row[0] # game_id + + + +#vim:fenc=utf-8:ts=4:sw=4:sta:noet:sts=4:fdm=marker:ai diff --git a/game.cgi b/game.cgi new file mode 100755 index 0000000..5feddda --- /dev/null +++ b/game.cgi @@ -0,0 +1,112 @@ +#!/usr/bin/env python3 + +import random + +from helpers import get_client_cert +from db import * +from urllib import parse + +TLS_CLIENT_HASH = get_client_cert() +user_id = check_hash(TLS_CLIENT_HASH) +user_name = get_name(user_id) or "[Not Set]" +game_id = get_game_id(user_id) or "[Not Set]" + +word=os.getenv("QUERY_STRING").upper() + +conn=create_connection() +with conn: + cur = conn.cursor() + cur.execute("SELECT board,words,graph FROM games WHERE id=?",(game_id,)) + row = cur.fetchone() + if row: + board=row[0] + words=row[1] + graph=row[2] + + a=board.split(',') + w=words.split(',') + g=graph.split(',') + + b=[] + for j in range(4): + b.append([]) + for i in range(4): + b[j].append(a[j*4+i]) + + print("```") + for j in range(4): + for i in range(4): + print(b[j][i],end=" ") + print() + print("```") + + if word: + i=-1 + for j in range(len(w)): + if w[j]==word: + i=j + break + if i!=-1: + if g[i]=='0': + g[i]='1' + + add_plays(game_id,user_id,words) + set_game_graph(game_id,','.join(g)) + + l=len(word) + if l==3 or l==4: p=1 + elif l==5: p=2 + elif l==6: p=3 + elif l==7: p=5 + elif l>=8: p=11 + + add_score(user_id,p) + score=get_score(user_id) + + print("you guessed",word,"plus",p,"points") + + s=1 + for t in g: + if t=='0': + s=0 + break + + if s==1: + set_game_stats(game_id,"STOP") + print("game is finished") + + else: + print(word,"is already guessed") + else: + print(word,"not found") + +print() + +print("game:",game_id) +print("username:",user_name) + +score=get_score(user_id) +print("your score is",score) + +g=get_game_graph(game_id).split(',') +lg=len(g) +c=0 +for t in g: + if t=='1': + c+=1 + + +print("words guessed",c,"left",lg-c,"total",lg) + + +print() + +stats=get_game_stats(game_id) +if stats=='PLAY': + print("=> guess.cgi guess a word") +elif stats=='STOP': + print("game is finished") + +print("=> lobby.cgi lobby") +print("=> new_game.cgi new game") +print("=> login.cgi login") diff --git a/guess.cgi b/guess.cgi new file mode 100755 index 0000000..cad74d4 --- /dev/null +++ b/guess.cgi @@ -0,0 +1,13 @@ +#!/usr/bin/env python3 + +import os +import random + +from helpers import get_query_string, get_client_cert,show_header_ok,show_query_string_required +from urllib import parse + +word=os.getenv("QUERY_STRING") +if not word: + word = show_query_string_required("Guess a word?") + +print("30 game.cgi?"+word,end='\r\n') diff --git a/helpers.py b/helpers.py new file mode 100644 index 0000000..48d2288 --- /dev/null +++ b/helpers.py @@ -0,0 +1,40 @@ +import os +import string +import random + +def get_client_cert(ok_if_found = True): + TLS_CLIENT_HASH = os.getenv('TLS_CLIENT_HASH') + if (TLS_CLIENT_HASH is None): + show_header_cert_required() + elif ok_if_found: + show_header_ok() + return TLS_CLIENT_HASH + +def get_query_string(msg, ok_if_found = True): + QUERY_STRING = os.getenv('QUERY_STRING') + if(not QUERY_STRING): + show_query_string_required(msg) + elif ok_if_found: + show_header_ok() + return QUERY_STRING + +def show_header_ok(): + print("20 text/gemini; charset=utf-8", end = "\r\n") + +def show_header_cert_required(): + print("60 text/gemini; charset=utf-8", end = "\r\n") + quit() + +def show_query_string_required(msg): + print("10 " + msg, end = "\r\n") + quit() + +def show_redirect(url): + print("30 " + url, end = "\r\n") + quit() + + +def id_generator(size=12, chars=string.ascii_uppercase + string.digits): + return ''.join(random.choice(chars) for _ in range(size)) + +#vim:fenc=utf-8:ts=4:sw=4:sta:noet:sts=4:fdm=marker:ai diff --git a/index.cgi b/index.cgi new file mode 100755 index 0000000..f1edff0 --- /dev/null +++ b/index.cgi @@ -0,0 +1,16 @@ +#!/usr/bin/env python3 + +from helpers import show_header_ok +show_header_ok() + +with open('cgi-bin/README.md') as f: + contents = f.read() + print(contents) + +print("## Project Source:") +print("=> https://tildegit.org/tomasino/gemspace/src/branch/master/cgi-example CGI Example Source on tildegit.org") +print("") +print("## Get Started") +print("=> login.cgi Login with your client certificate to begin") + +#vim:fenc=utf-8:ts=4:sw=4:sta:noet:sts=4:fdm=marker:ai diff --git a/lobby.cgi b/lobby.cgi new file mode 100755 index 0000000..f2b0480 --- /dev/null +++ b/lobby.cgi @@ -0,0 +1,28 @@ +#!/usr/bin/env python3 + +import random + +from helpers import get_client_cert +from db import * + +TLS_CLIENT_HASH = get_client_cert() +user_id = check_hash(TLS_CLIENT_HASH) +user_name = get_name(user_id) or "[Not Set]" + +print("username:",user_name) +print() + +print("=> login.cgi login") +print("=> new_game.cgi new game") + +print() + +conn=create_connection() +with conn: + cur = conn.cursor() + cur.execute("SELECT * FROM games ORDER BY stime DESC") + rows = cur.fetchall() + for row in rows: + print("=> choose.cgi?game_id=" + +str(row[0])+" Game " + +str(row[0]),end="\r\n") diff --git a/login.cgi b/login.cgi new file mode 100755 index 0000000..a8baff3 --- /dev/null +++ b/login.cgi @@ -0,0 +1,36 @@ +#!/usr/bin/env python3 + +# modules are all in the parent directory +import sys +# sys.path.append('..') + +# import helpers from modules +from helpers import get_client_cert +from db import * + +TLS_CLIENT_HASH = get_client_cert() +user_id = check_hash(TLS_CLIENT_HASH) +user_name = get_name(user_id) or "[Not Set]" + +print("# Welcome: User Logged In") +print() + +print("## Your USER ID:") +print(user_id) +print() + +print("## Your USER NAME:") +print(user_name) +print("=> change-name.cgi Change User Name") +print() + +print("## TLS Certs associated with account") +print("=> update-auth-code.cgi Generate an auth code to add another cert to your account") +print("=> add-cert.cgi Add another cert to your account (with auth code)") +print() + +print("=> new_game.cgi new game") +print("=> lobby.cgi lobby") + + +#vim:fenc=utf-8:ts=4:sw=4:sta:noet:sts=4:fdm=marker:ai diff --git a/new_game.cgi b/new_game.cgi new file mode 100755 index 0000000..99a4b3c --- /dev/null +++ b/new_game.cgi @@ -0,0 +1,105 @@ +#!/usr/bin/env python3 + +import random + +from helpers import get_query_string, get_client_cert,show_header_ok,show_redirect + +from db import check_hash, set_name, create_connection, create_new_game + + + +class Trie: + def __init__(self,letter): + self.letter=letter + self.mark=False + self.next=[None]*26 + @staticmethod + def add(root,word): + curr=root + for i in word: + j=i.upper() + k=ord(j)-65 + if curr.next[k]==None: + curr.next[k]=Trie(j) + curr=curr.next[k] + curr.mark=True + + + +f=open('cgi-bin/dice.txt','r') +dice=f.read().split('\n') +f.close() +dice=list(filter(None,dice)) + + + +random.shuffle(dice) +b=[] +for j in range(4): + b.append([]) + for i in range(4): + die=dice[j*4+i] + b[j].append(die[random.randrange(0,len(die))].upper()) + + + + +def r(s): + return s and len(s)>=3 and len(s)<=16 +f=open('cgi-bin/enable.txt','r') +words=f.read().split('\n') +f.close() +words=list(filter(r,words)) +trie=Trie(None) +for word in words: + Trie.add(trie,word.upper()) + + + +g=[] +for j in range(4): + g.append([]) + for i in range(4): + g[j].append(False) +found=[] +l=['' for i in range(16)] +def dfs(x,y,d,trie): + if x<0 or x>=4 or y<0 or y>=4: + return + if g[y][x]: + return + k=ord(b[y][x].upper())-65 + t=trie.next[k] + if not t: + return + l[d]=b[y][x].upper() + if t.mark: + w=''.join(l[0:d+1]) + if w not in found: + found.append(w) + g[y][x]=True + for j in [-1,0,1]: + for i in [-1,0,1]: + if i!=0 or j!=0: + dfs(x+i,y+j,d+1,t) + g[y][x]=False +for j in range(4): + for i in range(4): + dfs(i,j,0,trie) + +found.sort(key=len,reverse=True) + +board=[] +for j in range(4): + for i in range(4): + board.append(b[j][i]) +bstr=','.join(board) +fstr=','.join(found) +gstr=','.join(['0']*len(found)) + +conn=create_connection() +game_id=create_new_game(conn,bstr,fstr,gstr) + +show_redirect("choose.cgi?game_id="+str(game_id)) + + diff --git a/update-auth-code.cgi b/update-auth-code.cgi new file mode 100755 index 0000000..2134f7e --- /dev/null +++ b/update-auth-code.cgi @@ -0,0 +1,28 @@ +#!/usr/bin/env python3 + +# modules are all in the parent directory +import sys +# sys.path.append('..') + +# import helpers from modules +from helpers import get_client_cert, id_generator +from db import check_hash, set_add_key_code + +TLS_CLIENT_HASH = get_client_cert() +user_id = check_hash(TLS_CLIENT_HASH) +key_code = id_generator() +set_add_key_code(user_id, key_code) + +print("# Add Cert Page") +print() + +print("Your authorization code to add new client certificates has been updated. Visit the 'Add Cert' page from another device or with another key and enter your authorization code to connect the accounts.") +print() +print("## AUTH CODE") +print(key_code) +print("=> add-cert.cgi?" + key_code + " Link to Add Cert page with Auth Code") +print() +print("=> login.cgi Back to main page") + + +#vim:fenc=utf-8:ts=4:sw=4:sta:noet:sts=4:fdm=marker:ai