Merge pull request #787 from ennorehling/develop

bugfixes develop
This commit is contained in:
Enno Rehling 2018-06-11 21:44:58 +02:00 committed by GitHub
commit 136e093c96
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
20 changed files with 710 additions and 720 deletions

View file

@ -9,7 +9,7 @@ end_of_line = lf
insert_final_newline = true
# 4 space indentation
[*.{c,h,lua}]
[*.{c,h,lua,py}]
indent_style = space
indent_size = 4

View file

@ -1,5 +1,6 @@
install(PROGRAMS create-orders backup-eressea run-turn send-zip-report
send-bz2-report compress.py compress.sh epasswd.py orders-process
process-orders.py accept-orders.py
checkpasswd.py sendreport.sh sendreports.sh orders-accept DESTINATION bin)
install(DIRECTORY cron/ DESTINATION bin USE_SOURCE_PERMISSIONS

377
process/accept-orders.py Executable file
View file

@ -0,0 +1,377 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from email.Utils import parseaddr
from email.Parser import Parser
import os
import os.path
import ConfigParser
from re import compile, IGNORECASE
from stat import ST_MTIME
from string import upper, split, replace
import logging
import sys
import subprocess
from sys import stdin
from time import ctime, sleep, time
from socket import gethostname
from rfc822 import parsedate_tz, mktime_tz
if 'ERESSEA' in os.environ:
dir = os.environ['ERESSEA']
elif 'HOME' in os.environ:
dir = os.path.join(os.environ['HOME'], 'eressea')
else: # WTF? No HOME?
dir = "/home/eressea/eressea"
if not os.path.isdir(dir):
print "please set the ERESSEA environment variable to the install path"
sys.exit(1)
rootdir = dir
game = int(sys.argv[1])
gamedir = os.path.join(rootdir, "game-%d" % (game, ))
frommail = 'eressea-server@kn-bremen.de'
gamename = 'Eressea'
sender = '%s Server <%s>' % (gamename, frommail)
inifile = os.path.join(gamedir, 'eressea.ini')
if not os.path.exists(inifile):
print "no such file: " . inifile
else:
config = ConfigParser.ConfigParser()
config.read(inifile)
if config.has_option('game', 'email'):
frommail = config.get('game', 'email')
if config.has_option('game', 'name'):
gamename = config.get('game', 'name')
if config.has_option('game', 'sender'):
sender = config.get('game', 'sender')
else:
sender = "%s Server <%s>" % (gamename, frommail)
config = None
prefix = 'turn-'
hostname = gethostname()
orderbase = "orders.dir"
sendmail = True
# maximum number of reports per sender:
maxfiles = 20
# write headers to file?
writeheaders = True
# reject all html email?
rejecthtml = True
def unlock_file(filename):
try:
os.unlink(filename+".lock")
except:
print "could not unlock %s.lock, file not found" % filename
def lock_file(filename):
i = 0
wait = 1
if not os.path.exists(filename):
file=open(filename, "w")
file.close()
while True:
try:
os.symlink(filename, filename+".lock")
return
except:
i = i+1
if i == 5: unlock_file(filename)
sleep(wait)
wait = wait*2
messages = {
"multipart-en" :
"ERROR: The orders you sent contain no plaintext. " \
"The Eressea server cannot process orders containing HTML " \
"or invalid attachments, which are the reasons why this " \
"usually happens. Please change the settings of your mail " \
"software and re-send the orders.",
"multipart-de" :
"FEHLER: Die von dir eingeschickte Mail enthält keinen " \
"Text. Evtl. hast Du den Zug als HTML oder als anderweitig " \
"ungültig formatierte Mail ingeschickt. Wir können ihn " \
"deshalb nicht berücksichtigen. Schicke den Zug nochmals " \
"als reinen Text ohne Formatierungen ein.",
"maildate-de":
"Es erreichte uns bereits ein Zug mit einem späteren " \
"Absendedatum (%s > %s). Entweder ist deine " \
"Systemzeit verstellt, oder ein Zug hat einen anderen Zug von " \
"dir auf dem Transportweg überholt. Entscheidend für die " \
"Auswertungsreihenfolge ist das Absendedatum, d.h. der Date:-Header " \
"deiner Mail.",
"maildate-en":
"The server already received an order file that was sent at a later " \
"date (%s > %s). Either your system clock is wrong, or two messages have " \
"overtaken each other on the way to the server. The order of " \
"execution on the server is always according to the Date: header in " \
"your mail.",
"nodate-en":
"Your message did not contain a valid Date: header in accordance with RFC2822.",
"nodate-de":
"Deine Nachricht enthielt keinen gueltigen Date: header nach RFC2822.",
"error-de":
"Fehler",
"error-en":
"Error",
"warning-de":
"Warnung",
"warning-en":
"Warning",
"subject-de":
"Befehle angekommen",
"subject-en":
"orders received"
}
# return 1 if addr is a valid email address
def valid_email(addr):
rfc822_specials = '/()<>@,;:\\"[]'
# First we validate the name portion (name@domain)
c = 0
while c < len(addr):
if addr[c] == '"' and (not c or addr[c - 1] == '.' or addr[c - 1] == '"'):
c = c + 1
while c < len(addr):
if addr[c] == '"': break
if addr[c] == '\\' and addr[c + 1] == ' ':
c = c + 2
continue
if ord(addr[c]) < 32 or ord(addr[c]) >= 127: return 0
c = c + 1
else: return 0
if addr[c] == '@': break
if addr[c] != '.': return 0
c = c + 1
continue
if addr[c] == '@': break
if ord(addr[c]) <= 32 or ord(addr[c]) >= 127: return 0
if addr[c] in rfc822_specials: return 0
c = c + 1
if not c or addr[c - 1] == '.': return 0
# Next we validate the domain portion (name@domain)
domain = c = c + 1
if domain >= len(addr): return 0
count = 0
while c < len(addr):
if addr[c] == '.':
if c == domain or addr[c - 1] == '.': return 0
count = count + 1
if ord(addr[c]) <= 32 or ord(addr[c]) >= 127: return 0
if addr[c] in rfc822_specials: return 0
c = c + 1
return count >= 1
# return the replyto or from address in the header
def get_sender(header):
replyto = header.get("Reply-To")
if replyto is None:
replyto = header.get("From")
if replyto is None: return None
x = parseaddr(replyto)
return x[1]
# return first available filename basename,[0-9]+
def available_file(dirname, basename):
ver = 0
maxdate = 0
filename = "%s/%s,%s,%d" % (dirname, basename, hostname, ver)
while os.path.exists(filename):
maxdate = max(os.stat(filename)[ST_MTIME], maxdate)
ver = ver + 1
filename = "%s/%s,%s,%d" % (dirname, basename, hostname, ver)
if ver >= maxfiles:
return None, None
return maxdate, filename
def formatpar(string, l=76, indent=2):
words = split(string)
res = ""
ll = 0
first = 1
for word in words:
if first == 1:
res = word
first = 0
ll = len(word)
else:
if ll + len(word) > l:
res = res + "\n"+" "*indent+word
ll = len(word) + indent
else:
res = res+" "+word
ll = ll + len(word) + 1
return res+"\n"
def store_message(message, filename):
outfile = open(filename, "w")
outfile.write(message.as_string())
outfile.close()
return
def write_part(outfile, part):
charset = part.get_content_charset()
payload = part.get_payload(decode=True)
if charset is None:
charset = "latin1"
try:
msg = payload.decode(charset, "ignore")
except:
msg = payload
charset = None
try:
utf8 = msg.encode("utf-8", "ignore")
outfile.write(utf8)
except:
outfile.write(msg)
return False
outfile.write("\n");
return True
def copy_orders(message, filename, sender):
# print the header first
if writeheaders:
from os.path import split
dirname, basename = split(filename)
dirname = dirname + '/headers'
if not os.path.exists(dirname): os.mkdir(dirname)
outfile = open(dirname + '/' + basename, "w")
for name, value in message.items():
outfile.write(name + ": " + value + "\n")
outfile.close()
found = False
outfile = open(filename, "w")
if message.is_multipart():
for part in message.get_payload():
content_type = part.get_content_type()
logger.debug("found content type %s for %s" % (content_type, sender))
if content_type=="text/plain":
if write_part(outfile, part):
found = True
else:
charset = part.get_content_charset()
logger.error("could not write text/plain part (charset=%s) for %s" % (charset, sender))
else:
if write_part(outfile, message):
found = True
else:
charset = message.get_content_charset()
logger.error("could not write text/plain message (charset=%s) for %s" % (charset, sender))
outfile.close()
return found
# create a file, containing:
# game=0 locale=de file=/path/to/filename email=rcpt@domain.to
def accept(game, locale, stream, extend=None):
global rootdir, orderbase, gamedir, gamename, sender
if extend is not None:
orderbase = orderbase + ".pre-" + extend
savedir = os.path.join(gamedir, orderbase)
# check if it's one of the pre-sent orders.
# create the save-directories if they don't exist
if not os.path.exists(gamedir): os.mkdir(gamedir)
if not os.path.exists(savedir): os.mkdir(savedir)
# parse message
message = Parser().parse(stream)
email = get_sender(message)
logger = logging.getLogger(email)
# write syslog
if email is None or valid_email(email)==0:
logger.warning("invalid email address: " + str(email))
return -1
logger.info("received orders from " + email)
# get an available filename
lock_file(gamedir + "/orders.queue")
maxdate, filename = available_file(savedir, prefix + email)
if filename is None:
logger.warning("more than " + str(maxfiles) + " orders from " + email)
return -1
# copy the orders to the file
text_ok = copy_orders(message, filename, email)
unlock_file(gamedir + "/orders.queue")
warning, msg, fail = None, "", False
maildate = message.get("Date")
if maildate != None:
turndate = mktime_tz(parsedate_tz(maildate))
os.utime(filename, (turndate, turndate))
logger.debug("mail date is '%s' (%d)" % (maildate, turndate))
if False and turndate < maxdate:
logger.warning("inconsistent message date " + email)
warning = " (" + messages["warning-" + locale] + ")"
msg = msg + formatpar(messages["maildate-" + locale] % (ctime(maxdate),ctime(turndate)), 76, 2) + "\n"
else:
logger.warning("missing message date " + email)
warning = " (" + messages["warning-" + locale] + ")"
msg = msg + formatpar(messages["nodate-" + locale], 76, 2) + "\n"
if not text_ok:
warning = " (" + messages["error-" + locale] + ")"
msg = msg + formatpar(messages["multipart-" + locale], 76, 2) + "\n"
logger.warning("rejected - no text/plain in orders from " + email)
os.unlink(filename)
savedir = savedir + "/rejected"
if not os.path.exists(savedir): os.mkdir(savedir)
lock_file(gamedir + "/orders.queue")
maxdate, filename = available_file(savedir, prefix + email)
store_message(message, filename)
unlock_file(gamedir + "/orders.queue")
fail = True
if sendmail and warning is not None:
subject = gamename + " " + messages["subject-"+locale] + warning
print("mail " + subject)
ps = subprocess.Popen(['mutt', '-s', subject, email], stdin=subprocess.PIPE)
ps.communicate(msg)
if not sendmail:
print text_ok, fail, email
print filename
if not fail:
lock_file(gamedir + "/orders.queue")
queue = open(gamedir + "/orders.queue", "a")
queue.write("email=%s file=%s locale=%s game=%s\n" % (email, filename, locale, game))
queue.close()
unlock_file(gamedir + "/orders.queue")
logger.info("done - accepted orders from " + email)
return 0
# the main body of the script:
try:
os.mkdir(os.path.join(rootdir, 'log'))
except:
pass # already exists?
LOG_FILENAME=os.path.join(rootdir, 'log/orders.log')
logging.basicConfig(level=logging.DEBUG, filename=LOG_FILENAME)
logger = logging
delay = None # TODO: parse the turn delay
locale = sys.argv[2]
infile = stdin
if len(sys.argv)>3:
infile = open(sys.argv[3], "r")
retval = accept(game, locale, infile, delay)
if infile!=stdin:
infile.close()
sys.exit(retval)

View file

@ -1,378 +1,5 @@
#!/usr/bin/env python
# -*- coding: iso-8859-1 -*-
#!/bin/sh
SCRIPT=$(readlink -f $0)
cd $(dirname $SCRIPT)
python accept-orders.py "$@"
from email.Utils import parseaddr
from email.Parser import Parser
import os
import os.path
import ConfigParser
from re import compile, IGNORECASE
from stat import ST_MTIME
from string import upper, split, replace
import logging
import sys
from sys import stdin
from time import ctime, sleep, time
from socket import gethostname
from rfc822 import parsedate_tz, mktime_tz
if 'ERESSEA' in os.environ:
dir = os.environ['ERESSEA']
elif 'HOME' in os.environ:
dir = os.path.join(os.environ['HOME'], '/eressea')
else: # WTF? No HOME?
dir = "/home/eressea/eressea"
if not os.path.isdir(dir):
print "please set the ERESSEA environment variable to the install path"
sys.exit(1)
rootdir = dir
game = int(sys.argv[1])
gamedir = os.path.join(rootdir, "game-%d" % (game, ))
frommail = 'eressea-server@kn-bremen.de'
gamename = 'Eressea'
sender = '%s Server <%s>' % (gamename, frommail)
inifile = os.path.join(gamedir, 'eressea.ini')
if not os.path.exists(inifile):
print "no such file: " . inifile
else:
config = ConfigParser.ConfigParser()
config.read(inifile)
if config.has_option('game', 'email'):
frommail = config.get('game', 'email')
if config.has_option('game', 'name'):
gamename = config.get('game', 'name')
if config.has_option('game', 'sender'):
sender = config.get('game', 'sender')
else:
sender = "%s Server <%s>" % (gamename, frommail)
config = None
prefix = 'turn-'
hostname = gethostname()
orderbase = "orders.dir"
sendmail = True
# maximum number of reports per sender:
maxfiles = 20
# write headers to file?
writeheaders = True
# reject all html email?
rejecthtml = True
def unlock_file(filename):
try:
os.unlink(filename+".lock")
except:
print "could not unlock %s.lock, file not found" % filename
def lock_file(filename):
i = 0
wait = 1
if not os.path.exists(filename):
file=open(filename, "w")
file.close()
while True:
try:
os.symlink(filename, filename+".lock")
return
except:
i = i+1
if i == 5: unlock_file(filename)
sleep(wait)
wait = wait*2
messages = {
"multipart-en" :
"ERROR: The orders you sent contain no plaintext. " \
"The Eressea server cannot process orders containing HTML " \
"or invalid attachments, which are the reasons why this " \
"usually happens. Please change the settings of your mail " \
"software and re-send the orders.",
"multipart-de" :
"FEHLER: Die von dir eingeschickte Mail enth<74>lt keinen " \
"Text. Evtl. hast Du den Zug als HTML oder als anderweitig " \
"ung<6E>ltig formatierte Mail ingeschickt. Wir k<>nnen ihn " \
"deshalb nicht ber<65>cksichtigen. Schicke den Zug nochmals " \
"als reinen Text ohne Formatierungen ein.",
"maildate-de":
"Es erreichte uns bereits ein Zug mit einem sp<73>teren " \
"Absendedatum (%s > %s). Entweder ist deine " \
"Systemzeit verstellt, oder ein Zug hat einen anderen Zug von " \
"dir auf dem Transportweg <20>berholt. Entscheidend f<>r die " \
"Auswertungsreihenfolge ist das Absendedatum, d.h. der Date:-Header " \
"deiner Mail.",
"maildate-en":
"The server already received an order file that was sent at a later " \
"date (%s > %s). Either your system clock is wrong, or two messages have " \
"overtaken each other on the way to the server. The order of " \
"execution on the server is always according to the Date: header in " \
"your mail.",
"nodate-en":
"Your message did not contain a valid Date: header in accordance with RFC2822.",
"nodate-de":
"Deine Nachricht enthielt keinen gueltigen Date: header nach RFC2822.",
"error-de":
"Fehler",
"error-en":
"Error",
"warning-de":
"Warnung",
"warning-en":
"Warning",
"subject-de":
"Befehle angekommen",
"subject-en":
"orders received"
}
# return 1 if addr is a valid email address
def valid_email(addr):
rfc822_specials = '/()<>@,;:\\"[]'
# First we validate the name portion (name@domain)
c = 0
while c < len(addr):
if addr[c] == '"' and (not c or addr[c - 1] == '.' or addr[c - 1] == '"'):
c = c + 1
while c < len(addr):
if addr[c] == '"': break
if addr[c] == '\\' and addr[c + 1] == ' ':
c = c + 2
continue
if ord(addr[c]) < 32 or ord(addr[c]) >= 127: return 0
c = c + 1
else: return 0
if addr[c] == '@': break
if addr[c] != '.': return 0
c = c + 1
continue
if addr[c] == '@': break
if ord(addr[c]) <= 32 or ord(addr[c]) >= 127: return 0
if addr[c] in rfc822_specials: return 0
c = c + 1
if not c or addr[c - 1] == '.': return 0
# Next we validate the domain portion (name@domain)
domain = c = c + 1
if domain >= len(addr): return 0
count = 0
while c < len(addr):
if addr[c] == '.':
if c == domain or addr[c - 1] == '.': return 0
count = count + 1
if ord(addr[c]) <= 32 or ord(addr[c]) >= 127: return 0
if addr[c] in rfc822_specials: return 0
c = c + 1
return count >= 1
# return the replyto or from address in the header
def get_sender(header):
replyto = header.get("Reply-To")
if replyto is None:
replyto = header.get("From")
if replyto is None: return None
x = parseaddr(replyto)
return x[1]
# return first available filename basename,[0-9]+
def available_file(dirname, basename):
ver = 0
maxdate = 0
filename = "%s/%s,%s,%d" % (dirname, basename, hostname, ver)
while os.path.exists(filename):
maxdate = max(os.stat(filename)[ST_MTIME], maxdate)
ver = ver + 1
filename = "%s/%s,%s,%d" % (dirname, basename, hostname, ver)
if ver >= maxfiles:
return None, None
return maxdate, filename
def formatpar(string, l=76, indent=2):
words = split(string)
res = ""
ll = 0
first = 1
for word in words:
if first == 1:
res = word
first = 0
ll = len(word)
else:
if ll + len(word) > l:
res = res + "\n"+" "*indent+word
ll = len(word) + indent
else:
res = res+" "+word
ll = ll + len(word) + 1
return res+"\n"
def store_message(message, filename):
outfile = open(filename, "w")
outfile.write(message.as_string())
outfile.close()
return
def write_part(outfile, part):
charset = part.get_content_charset()
payload = part.get_payload(decode=True)
if charset is None:
charset = "latin1"
try:
msg = payload.decode(charset, "ignore")
except:
msg = payload
charset = None
try:
utf8 = msg.encode("utf-8", "ignore")
outfile.write(utf8)
except:
outfile.write(msg)
return False
outfile.write("\n");
return True
def copy_orders(message, filename, sender):
# print the header first
if writeheaders:
from os.path import split
dirname, basename = split(filename)
dirname = dirname + '/headers'
if not os.path.exists(dirname): os.mkdir(dirname)
outfile = open(dirname + '/' + basename, "w")
for name, value in message.items():
outfile.write(name + ": " + value + "\n")
outfile.close()
found = False
outfile = open(filename, "w")
if message.is_multipart():
for part in message.get_payload():
content_type = part.get_content_type()
logger.debug("found content type %s for %s" % (content_type, sender))
if content_type=="text/plain":
if write_part(outfile, part):
found = True
else:
charset = part.get_content_charset()
logger.error("could not write text/plain part (charset=%s) for %s" % (charset, sender))
else:
if write_part(outfile, message):
found = True
else:
charset = message.get_content_charset()
logger.error("could not write text/plain message (charset=%s) for %s" % (charset, sender))
outfile.close()
return found
# create a file, containing:
# game=0 locale=de file=/path/to/filename email=rcpt@domain.to
def accept(game, locale, stream, extend=None):
global rootdir, orderbase, gamedir, gamename, sender
if extend is not None:
orderbase = orderbase + ".pre-" + extend
savedir = os.path.join(gamedir, orderbase)
# check if it's one of the pre-sent orders.
# create the save-directories if they don't exist
if not os.path.exists(gamedir): os.mkdir(gamedir)
if not os.path.exists(savedir): os.mkdir(savedir)
# parse message
message = Parser().parse(stream)
email = get_sender(message)
logger = logging.getLogger(email)
# write syslog
if email is None or valid_email(email)==0:
logger.warning("invalid email address: " + str(email))
return -1
logger.info("received orders from " + email)
# get an available filename
lock_file(gamedir + "/orders.queue")
maxdate, filename = available_file(savedir, prefix + email)
if filename is None:
logger.warning("more than " + str(maxfiles) + " orders from " + email)
return -1
# copy the orders to the file
text_ok = copy_orders(message, filename, email)
unlock_file(gamedir + "/orders.queue")
warning, msg, fail = None, "", False
maildate = message.get("Date")
if maildate != None:
turndate = mktime_tz(parsedate_tz(maildate))
os.utime(filename, (turndate, turndate))
logger.debug("mail date is '%s' (%d)" % (maildate, turndate))
if turndate < maxdate:
logger.warning("inconsistent message date " + email)
warning = " (" + messages["warning-" + locale] + ")"
msg = msg + formatpar(messages["maildate-" + locale] % (ctime(maxdate),ctime(turndate)), 76, 2) + "\n"
else:
logger.warning("missing message date " + email)
warning = " (" + messages["warning-" + locale] + ")"
msg = msg + formatpar(messages["nodate-" + locale], 76, 2) + "\n"
if not text_ok:
warning = " (" + messages["error-" + locale] + ")"
msg = msg + formatpar(messages["multipart-" + locale], 76, 2) + "\n"
logger.warning("rejected - no text/plain in orders from " + email)
os.unlink(filename)
savedir = savedir + "/rejected"
if not os.path.exists(savedir): os.mkdir(savedir)
lock_file(gamedir + "/orders.queue")
maxdate, filename = available_file(savedir, prefix + email)
store_message(message, filename)
unlock_file(gamedir + "/orders.queue")
fail = True
if sendmail and warning is not None:
subject = gamename + " " + messages["subject-"+locale] + warning
mail = "Subject: %s\nFrom: %s\nTo: %s\n\n" % (subject, sender, email) + msg
from smtplib import SMTP
server = SMTP("localhost")
server.sendmail(sender, email, mail)
server.close()
if not sendmail:
print text_ok, fail, email
print filename
if not fail:
lock_file(gamedir + "/orders.queue")
queue = open(gamedir + "/orders.queue", "a")
queue.write("email=%s file=%s locale=%s game=%s\n" % (email, filename, locale, game))
queue.close()
unlock_file(gamedir + "/orders.queue")
logger.info("done - accepted orders from " + email)
return 0
# the main body of the script:
try:
os.mkdir(os.path.join(rootdir, 'log'))
except:
pass # already exists?
LOG_FILENAME=os.path.join(rootdir, 'log/orders.log')
logging.basicConfig(level=logging.DEBUG, filename=LOG_FILENAME)
logger = logging
delay=None # TODO: parse the turn delay
locale = sys.argv[2]
infile = stdin
if len(sys.argv)>3:
infile = open(sys.argv[3], "r")
retval = accept(game, locale, infile, delay)
if infile!=stdin:
infile.close()
sys.exit(retval)

View file

@ -1,242 +1,5 @@
#!/usr/bin/env python
# -*- coding: iso-8859-1 -*-
#!/bin/sh
SCRIPT=$(readlink -f $0)
cd $(dirname $SCRIPT)
python process-orders.py "$@"
from os import unlink, symlink, rename, popen, tmpfile
import sys
import os
import os.path
import ConfigParser
from re import compile, IGNORECASE
from string import split, join, upper, strip
from sys import argv, exit
from time import sleep, time, ctime
from syslog import openlog, closelog, syslog
from epasswd import EPasswd
def pwd_get_email(faction, pwd, pwdfile=None):
return None
def split_filename(filename):
return os.path.split(filename)
def unlock_file(filename):
try:
unlink(filename+".lock")
except:
print "could not unlock %s.lock, file not found" % filename
raise
def lock_file(filename):
i = 0
wait = 1
if not os.path.exists(filename):
file=open(filename, "w")
file.close()
while True:
try:
symlink(filename, filename+".lock")
return
except:
i = i+1
if i == 5:
raise
sleep(wait)
wait = wait*2
messages = {
"subject-de": "Befehle angekommen",
"subject-en": "orders received",
"validate-en": "Validating",
"validate-de": "Verarbeite",
"faction-en": "Faction",
"faction-de": "Partei",
"unknown-de": "WARNUNG: Die Partei ist nicht bekannt, oder das Passwort falsch!",
"unknown-en": "WARNING: This faction is unknown, or the password is incorrect!",
"warning-de": "Warnung",
"warning-en": "Warning",
"error-de": "Fehler",
"error-en": "Error",
}
game = int(sys.argv[1])
echeck_cmd = "/home/eressea/echeck/echeck.sh"
maxlines = 25
# base directory for all your games:
install_dir = "/home/eressea/eressea"
if 'ERESSEA' in os.environ:
install_dir = os.environ['ERESSEA']
elif 'HOME' in os.environ:
install_dir = os.path.join(os.environ['HOME'], '/eressea')
if not os.path.isdir(install_dir):
print "please set the ERESSEA environment variable to the install path"
sys.exit(1)
game_dir = os.path.join(install_dir, "game-%d" % (game, ))
frommail = 'eressea-server@kn-bremen.de'
gamename = 'Eressea'
sender = '%s Server <%s>' % (gamename, frommail)
inifile = os.path.join(game_dir, 'eressea.ini')
if not os.path.exists(inifile):
print "no such file: " . inifile
else:
config = ConfigParser.ConfigParser()
config.read(inifile)
if config.has_option('game', 'email'):
frommail = config.get('game', 'email')
if config.has_option('game', 'name'):
gamename = config.get('game', 'name')
if config.has_option('game', 'sender'):
sender = config.get('game', 'sender')
else:
sender = "%s Server <%s>" % (gamename, frommail)
config = None
queue_file = os.path.join(game_dir, "orders.queue")
if not os.path.exists(queue_file):
exit(0)
# regular expression that finds the start of a faction
fact_re = compile("^\s*(eressea|partei|faction)\s+([a-zA-Z0-9]+)\s+\"?([^\"]*)\"?", IGNORECASE)
def check_pwd(filename, email, pw_data):
results = []
try:
file = open(filename, "r")
except:
print "could not open file", filename
return results
for line in file.readlines():
mo = fact_re.search(strip(line))
if mo != None:
fact_nr = str(mo.group(2))
fact_pw = str(mo.group(3))
if pw_data.fac_exists(fact_nr):
if not pw_data.check(fact_nr, fact_pw):
game_email = pw_data.get_email(fact_nr)
results = results + [ (fact_nr, game_email, False, fact_pw) ]
else:
game_email = pw_data.get_email(fact_nr)
results = results + [ (fact_nr, game_email, True, fact_pw) ]
else:
results = results + [ (fact_nr, None, False, fact_pw) ]
return results
def echeck(filename, locale, rules):
dirname, filename = split_filename(filename)
stream = popen("%s %s %s %s %s" % (echeck_cmd, locale, filename, dirname, rules), 'r')
lines = stream.readlines()
if len(lines)==0:
stream.close()
return None
if len(lines)>maxlines:
mail = join(lines[:maxlines-3] + ["...", "\n"] + lines[-3:], '')
else:
mail = join(lines[:maxlines], '')
stream.close()
return mail
# parse the queue file -
#print "connecting to SMTP..."
from smtplib import SMTP
try:
server = SMTP("localhost")
except:
print "could not connect to SMTP server"
exit(0)
#print "reading password file..."
pw_data = EPasswd(os.path.join(game_dir,"passwd"))
#print "reading orders.queue..."
# move the queue file to a save space while locking it:
try:
lock_file(queue_file)
except:
exit(0)
queuefile = open(queue_file, "r")
lines = queuefile.readlines()
queuefile.close()
# copy to a temp file
tname="/tmp/orders.queue.%s" % str(time())
try:
lock_file(tname)
except:
exit(0)
tmpfile=open(tname, "w")
for line in lines:
tmpfile.write(line)
tmpfile.close()
openlog("orders")
unlink(queue_file)
try:
unlock_file(queue_file)
except:
pass
for line in lines:
tokens = split(line[:-1], ' ')
dict = {}
for token in tokens:
name, value = split(token, '=')
dict[name] = value
email = dict["email"]
locale = dict["locale"]
game = int(dict["game"])
infile = dict["file"]
gamename='[E%d]' % game
rules='e%d' % game
warning = ""
failed = True
results = check_pwd(infile, email, pw_data)
logfile = open(os.path.join(game_dir, "zug.log"), "a")
dirname, filename = split_filename(infile)
msg = messages["validate-"+locale] + " " + infile + "\n\n"
for faction, game_email, success, pwd in results:
msg = msg + messages["faction-"+locale] + " " + faction + "\n"
if success: failed = False
else: msg = msg + messages["unknown-"+locale] + "\n"
msg = msg + "\n"
logfile.write("%s:%s:%s:%s:%s:%s\n" % (ctime(time()), email, game_email, faction, pwd, success))
logfile.close()
if failed:
warning = " (" + messages["warning-" + locale] + ")"
syslog("failed - no valid password in " + infile)
else:
result = None
if os.path.exists(echeck_cmd):
result = echeck(infile, locale, rules)
if result is None:
# echeck did not finish
msg = msg + "Echeck is broken. Your turn was accepted, but could not be verified.\n"
warning = " (" + messages["warning-" + locale] + ")"
syslog("process - echeck broken, " + infile)
else:
msg = msg + result
syslog("process - checked orders in " + infile)
subject = gamename + " " + messages["subject-" + locale] + warning
msg = "Subject: %s\nFrom: %s\nTo: %s\nContent-Type: text/plain; charset=utf-8\n\n" % (subject, sender, email) + msg
try:
server.sendmail(sender, email, msg)
except:
syslog("failed - cannot send to " + email)
server.close()
closelog()
unlink(tname)
unlock_file(tname)

215
process/process-orders.py Executable file
View file

@ -0,0 +1,215 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from os import unlink, symlink, rename, popen, tmpfile
import sys
import os
import os.path
import ConfigParser
import subprocess
from re import compile, IGNORECASE
from string import split, join, upper, strip
from sys import argv, exit
from time import sleep, time, ctime
from syslog import openlog, closelog, syslog
from epasswd import EPasswd
def pwd_get_email(faction, pwd, pwdfile=None):
return None
def split_filename(filename):
return os.path.split(filename)
def unlock_file(filename):
try:
unlink(filename+".lock")
except:
print "could not unlock %s.lock, file not found" % filename
raise
def lock_file(filename):
i = 0
wait = 1
if not os.path.exists(filename):
file=open(filename, "w")
file.close()
while True:
try:
symlink(filename, filename+".lock")
return
except:
i = i+1
if i == 5:
raise
sleep(wait)
wait = wait*2
messages = {
"subject-de": "Befehle angekommen",
"subject-en": "orders received",
"validate-en": "Validating",
"validate-de": "Verarbeite",
"faction-en": "Faction",
"faction-de": "Partei",
"unknown-de": "WARNUNG: Die Partei ist nicht bekannt, oder das Passwort falsch!",
"unknown-en": "WARNING: This faction is unknown, or the password is incorrect!",
"warning-de": "Warnung",
"warning-en": "Warning",
"error-de": "Fehler",
"error-en": "Error",
}
game = int(sys.argv[1])
echeck_cmd = "/home/eressea/echeck/echeck.sh"
maxlines = 25
# base directory for all your games:
install_dir = "/home/eressea/eressea"
if 'ERESSEA' in os.environ:
install_dir = os.environ['ERESSEA']
elif 'HOME' in os.environ:
install_dir = os.path.join(os.environ['HOME'], 'eressea')
if not os.path.isdir(install_dir):
print "please set the ERESSEA environment variable to the install path"
sys.exit(1)
game_dir = os.path.join(install_dir, "game-%d" % (game, ))
gamename = 'Eressea'
inifile = os.path.join(game_dir, 'eressea.ini')
queue_file = os.path.join(game_dir, "orders.queue")
if not os.path.exists(queue_file):
exit(0)
# regular expression that finds the start of a faction
fact_re = compile("^\s*(eressea|partei|faction)\s+([a-zA-Z0-9]+)\s+\"?([^\"]*)\"?", IGNORECASE)
def check_pwd(filename, email, pw_data):
results = []
try:
file = open(filename, "r")
except:
print "could not open file", filename
return results
for line in file.readlines():
mo = fact_re.search(strip(line))
if mo != None:
fact_nr = str(mo.group(2))
fact_pw = str(mo.group(3))
if pw_data.fac_exists(fact_nr):
if not pw_data.check(fact_nr, fact_pw):
game_email = pw_data.get_email(fact_nr)
results = results + [ (fact_nr, game_email, False, fact_pw) ]
else:
game_email = pw_data.get_email(fact_nr)
results = results + [ (fact_nr, game_email, True, fact_pw) ]
else:
results = results + [ (fact_nr, None, False, fact_pw) ]
return results
def echeck(filename, locale, rules):
dirname, filename = split_filename(filename)
stream = popen("%s %s %s %s %s" % (echeck_cmd, locale, filename, dirname, rules), 'r')
lines = stream.readlines()
if len(lines)==0:
stream.close()
return None
if len(lines)>maxlines:
mail = join(lines[:maxlines-3] + ["...", "\n"] + lines[-3:], '')
else:
mail = join(lines[:maxlines], '')
stream.close()
return mail
#print "reading password file..."
pw_data = EPasswd(os.path.join(game_dir,"passwd"))
#print "reading orders.queue..."
# move the queue file to a save space while locking it:
try:
lock_file(queue_file)
except:
exit(0)
queuefile = open(queue_file, "r")
lines = queuefile.readlines()
queuefile.close()
# copy to a temp file
tname="/tmp/orders.queue.%s" % str(time())
try:
lock_file(tname)
except:
exit(0)
tmpfile=open(tname, "w")
for line in lines:
tmpfile.write(line)
tmpfile.close()
openlog("orders")
unlink(queue_file)
try:
unlock_file(queue_file)
except:
pass
for line in lines:
tokens = split(line[:-1], ' ')
dict = {}
for token in tokens:
name, value = split(token, '=')
dict[name] = value
email = dict["email"]
locale = dict["locale"]
game = int(dict["game"])
infile = dict["file"]
gamename='[E%d]' % game
rules='e%d' % game
warning = ""
failed = True
results = check_pwd(infile, email, pw_data)
logfile = open(os.path.join(game_dir, "zug.log"), "a")
dirname, filename = split_filename(infile)
msg = messages["validate-"+locale] + " " + infile + "\n\n"
for faction, game_email, success, pwd in results:
msg = msg + messages["faction-"+locale] + " " + faction + "\n"
if success: failed = False
else: msg = msg + messages["unknown-"+locale] + "\n"
msg = msg + "\n"
logfile.write("%s:%s:%s:%s:%s:%s\n" % (ctime(time()), email, game_email, faction, pwd, success))
logfile.close()
if failed:
warning = " (" + messages["warning-" + locale] + ")"
syslog("failed - no valid password in " + infile)
else:
result = None
if os.path.exists(echeck_cmd):
result = echeck(infile, locale, rules)
if result is None:
# echeck did not finish
msg = msg + "Echeck is broken. Your turn was accepted, but could not be verified.\n"
warning = " (" + messages["warning-" + locale] + ")"
syslog("process - echeck broken, " + infile)
else:
msg = msg + result
syslog("process - checked orders in " + infile)
subject = gamename + " " + messages["subject-" + locale] + warning
try:
sp = subprocess.Popen(['mutt', '-s', subject, email], stdin=subprocess.PIPE)
sp.communicate(msg)
except:
syslog("failed - cannot send to " + email)
closelog()
unlink(tname)
unlock_file(tname)

View file

@ -698,10 +698,16 @@
<arg name="faction" type="faction"/>
</type>
</message>
<message name="nr_borderlist_postfix" section="nr">
<message name="nr_border_transparent" section="nr">
<type>
<arg name="transparent" type="int"/>
<arg name="object" type="string"/>
<arg name="dir" type="direction"/>
</type>
</message>
<message name="nr_border_opaque" section="nr">
<type>
<arg name="object" type="string"/>
<arg name="dir" type="direction"/>
</type>
</message>
<message name="nr_building_besieged" section="nr">
@ -5494,12 +5500,12 @@
<arg name="unit" type="unit"/>
</type>
</message>
<message name="tactics_lost" section="battle">
<message name="para_tactics_lost" section="battle">
<type>
<arg name="unit" type="unit"/>
</type>
</message>
<message name="tactics_won" section="battle">
<message name="para_tactics_won" section="battle">
<type>
<arg name="unit" type="unit"/>
</type>

View file

@ -1355,8 +1355,11 @@ msgstr "\"Es herrscht eine fröhliche und ausgelassene Stimmung. ($int36($id))\"
msgid "buildroad"
msgstr "\"$unit($unit) erweitert in $region($region) das Straßennetz um $int($size).\""
msgid "nr_borderlist_postfix"
msgstr "\"$if($transparent,\" befindet sich\",\" versperrt\") ${object}$if($transparent,\"\",\" die Sicht\").\""
msgid "nr_border_opaque"
msgstr "Im $direction($dir) verperrt ${object} die Sicht."
msgid "nr_border_transparent"
msgstr "Im $direction($dir) befindet sich ${object}."
msgid "effectstrength"
msgstr "\"$unit($mage) erhöht die Körperkraft von $unit.dative($target) beträchtlich.\""

View file

@ -1355,8 +1355,11 @@ msgstr "\"Everyone in this region seems to be having a very good time. ($int36($
msgid "buildroad"
msgstr "\"$unit($unit) extends the road network in $region($region) by $int($size).\""
msgid "nr_borderlist_postfix"
msgstr "\"$if($transparent,\" there is\",\" sight is blocked by \") ${object}.\""
msgid "nr_border_opaque"
msgstr "To the $direction($dir), ${object} is blocking the view."
msgid "nr_border_transparent"
msgstr "To the $direction($dir) is ${object}."
msgid "effectstrength"
msgstr "\"$unit($mage) increases the strength of $unit($target) dramatically.\""

View file

@ -1906,9 +1906,6 @@ msgstr "Mallorn"
msgid "castle"
msgstr "Burg"
msgid "nr_borderlist_infix"
msgstr ", im"
msgctxt "race"
msgid "shadowbat_p"
msgstr "Todesflattern"
@ -3706,9 +3703,6 @@ msgctxt "spell"
msgid "analyse_object"
msgstr "Lied des Ortes analysieren"
msgid "nr_borderlist_lastfix"
msgstr "und im"
msgctxt "race"
msgid "shadowknight_d"
msgstr "Schattenrittern"
@ -5573,7 +5567,7 @@ msgid "swamp_trail"
msgstr "der Sumpf von %s"
msgid "nr_nb_final"
msgstr "und im"
msgstr "und im "
msgid "aoc"
msgstr "Katzenamulett"

View file

@ -1642,9 +1642,6 @@ msgstr "mallorn"
msgid "castle"
msgstr "castle"
msgid "nr_borderlist_infix"
msgstr ", to the "
msgctxt "race"
msgid "shadowbat_p"
msgstr "darkbats"
@ -3261,9 +3258,6 @@ msgctxt "spell"
msgid "analyse_object"
msgstr "Analysis"
msgid "nr_borderlist_lastfix"
msgstr ", and to the "
msgctxt "race"
msgid "shadowknight_d"
msgstr "shadow knights"

View file

@ -491,8 +491,29 @@ function test_dwarf_mining()
local f = faction.create('dwarf')
local r = region.create(0, 0, 'plain')
local u = unit.create(f, r)
u.name = 'Xolgrim'
u:set_skill('mining', 2)
assert_equal(2, u:get_skill('mining'))
assert_equal(4, u:eff_skill('mining'))
end
function test_buy_sell()
local f = faction.create('human')
local r = region.create(0, 0, 'plain')
local u = unit.create(f, r)
local lux = r.luxury
local b = building.create(r, 'castle')
b.size = 10
u:set_skill('trade', 1)
item = 'silk'
name = 'Seide'
if lux == 'silk' then
item = 'balm'
name = 'Balsam'
end
u:add_item(item, 5)
u:add_order('VERKAUFE 1 ' .. name)
assert_equal(0, u:get_item('money'))
process_orders()
assert_equal(4, u:get_item(item))
assert_not_equal(0, u:get_item('money'))
end

View file

@ -818,7 +818,9 @@ static void start_resources(parseinfo *pi, const XML_Char *el, const XML_Char **
handle_requirement(pi, el, attr);
}
else if (xml_strcmp(el, "luxury") == 0) {
rtype->ltype = new_luxurytype(itype, 0);
int price = atoi(attr_get(attr, "price"));
assert(price > 0);
rtype->ltype = new_luxurytype(itype, price);
}
else if (xml_strcmp(el, "potion") == 0) {
int i, level = 0;
@ -1090,9 +1092,13 @@ static void start_races(parseinfo *pi, const XML_Char *el, const XML_Char **attr
name = attr_get(attr, "name");
if (name) {
int i;
assert(!rc);
pi->object = rc = rc_get_or_create(name);
int i;
while (AT_NONE != rc->attack[nattacks].type) {
++nattacks;
}
for (i = 0; attr[i]; i += 2) {
const XML_Char *key = attr[i], *val = attr[i + 1];

View file

@ -188,7 +188,7 @@ border_type *find_bordertype(const char *name)
{
border_type *bt = bordertypes;
while (bt && strcmp(bt->__name, name)!=0)
while (bt && strcmp(bt->_name, name)!=0)
bt = bt->next;
return bt;
}
@ -563,7 +563,7 @@ void write_borders(struct storage *store)
for (b = bhash; b != NULL; b = b->next) {
if (b->type->valid && !b->type->valid(b))
continue;
WRITE_TOK(store, b->type->__name);
WRITE_TOK(store, b->type->_name);
WRITE_INT(store, b->id);
WRITE_INT(store, b->from->uid);
WRITE_INT(store, b->to->uid);
@ -614,7 +614,7 @@ int read_borders(gamedata *data)
if (to == from && from) {
direction_t dir = (direction_t)(rng_int() % MAXDIRECTIONS);
region *r = rconnect(from, dir);
log_error("[read_borders] invalid %s in %s\n", type->__name, regionname(from, NULL));
log_error("[read_borders] invalid %s in %s\n", type->_name, regionname(from, NULL));
if (r != NULL)
to = r;
}
@ -633,5 +633,5 @@ int read_borders(gamedata *data)
}
const char * border_name(const connection *co, const struct region * r, const struct faction * f, int flags) {
return (co->type->name) ? co->type->name(co, r, f, flags) : co->type->__name;
return (co->type->name) ? co->type->name(co, r, f, flags) : co->type->_name;
}

View file

@ -45,7 +45,7 @@ extern "C" {
} connection;
typedef struct border_type {
const char *__name; /* internal use only */
const char *_name; /* internal use only */
variant_type datatype;
bool(*transparent) (const connection *, const struct faction *);
/* is it possible to see through this? */

View file

@ -73,7 +73,6 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
#include <util/assert.h>
#include <util/attrib.h>
#include <util/base36.h>
#include <util/bsdstring.h>
#include <util/gamedata.h>
#include <util/language.h>
#include <util/lists.h>
@ -1047,15 +1046,17 @@ int movewhere(const unit * u, const char *token, region * r, region ** resultp)
order * cycle_route(order * ord, const struct locale *lang, int gereist)
{
int cm = 0;
char tail[1024], *bufp = tail;
char neworder[2048], *obuf = neworder;
char tail[1024];
char neworder[2048];
char token[128];
direction_t d = NODIRECTION;
bool paused = false;
bool pause;
order *norder;
size_t size = sizeof(tail) - 1;
sbstring sbtail;
sbstring sborder;
sbs_init(&sbtail, tail, sizeof(tail));
sbs_init(&sborder, neworder, sizeof(neworder));
assert(getkeyword(ord) == K_ROUTE);
tail[0] = '\0';
neworder[0] = '\0';
@ -1063,14 +1064,10 @@ order * cycle_route(order * ord, const struct locale *lang, int gereist)
for (cm = 0;; ++cm) {
const char *s;
pause = false;
s = gettoken(token, sizeof(token));
if (s && *s) {
d = get_direction(s, lang);
if (d == D_PAUSE) {
pause = true;
}
else if (d == NODIRECTION) {
if (d == NODIRECTION) {
break;
}
}
@ -1079,38 +1076,37 @@ order * cycle_route(order * ord, const struct locale *lang, int gereist)
}
if (cm < gereist) {
/* TODO: hier sollte keine PAUSE auftreten */
assert(!pause);
if (!pause) {
assert (d != D_PAUSE);
if (d != D_PAUSE) {
const char *loc = LOC(lang, shortdirections[d]);
assert(loc);
if (bufp != tail) {
bufp = STRLCPY_EX(bufp, " ", &size, "cycle_route");
if (sbs_length(&sbtail) > 0) {
sbs_strcat(&sbtail, " ");
}
bufp = STRLCPY_EX(bufp, loc, &size, "cycle_route");
sbs_strcat(&sbtail, loc);
}
}
else if (strlen(neworder) > sizeof(neworder) / 2)
break;
else if (cm == gereist && !paused && pause) {
else if (cm == gereist && !paused && (d == D_PAUSE)) {
const char *loc = LOC(lang, parameters[P_PAUSE]);
bufp = STRLCPY_EX(bufp, " ", &size, "cycle_route");
bufp = STRLCPY_EX(bufp, loc, &size, "cycle_route");
sbs_strcat(&sbtail, " ");
sbs_strcat(&sbtail, loc);
paused = true;
}
else if (pause) {
/* da PAUSE nicht in ein shortdirections[d] umgesetzt wird (ist
* hier keine normale direction), muss jede PAUSE einzeln
* herausgefiltert und explizit gesetzt werden */
if (neworder != obuf) {
obuf += str_strlcat(obuf, " ", sizeof(neworder) - (obuf - neworder));
}
obuf += str_strlcat(obuf, LOC(lang, parameters[P_PAUSE]), sizeof(neworder) - (obuf - neworder));
}
else {
if (neworder != obuf) {
obuf += str_strlcat(obuf, " ", sizeof(neworder) - (obuf - neworder));
if (sbs_length(&sbtail) > 0) {
sbs_strcat(&sborder, " ");
}
if (d == D_PAUSE) {
/* da PAUSE nicht in ein shortdirections[d] umgesetzt wird (ist
* hier keine normale direction), muss jede PAUSE einzeln
* herausgefiltert und explizit gesetzt werden */
sbs_strcat(&sborder, LOC(lang, parameters[P_PAUSE]));
}
else {
sbs_strcat(&sborder, LOC(lang, shortdirections[d]));
}
obuf += str_strlcat(obuf, LOC(lang, shortdirections[d]), sizeof(neworder) - (obuf - neworder));
}
}
@ -2250,10 +2246,9 @@ static direction_t hunted_dir(attrib * at, int id)
int follow_ship(unit * u, order * ord)
{
region *rc = u->region;
size_t bytes;
sbstring sbcmd;
int moves, id, speed;
char command[256], *bufp = command;
size_t size = sizeof(command);
char command[256];
direction_t dir;
if (fval(u, UFL_NOTMOVING)) {
@ -2289,11 +2284,10 @@ int follow_ship(unit * u, order * ord)
return 0;
}
bufp = command;
bytes = slprintf(bufp, size, "%s %s", LOC(u->faction->locale, keyword(K_MOVE)), LOC(u->faction->locale, directions[dir]));
assert(bytes <= INT_MAX);
if (wrptr(&bufp, &size, (int)bytes) != 0)
WARN_STATIC_BUFFER();
sbs_init(&sbcmd, command, sizeof(command));
sbs_strcpy(&sbcmd, LOC(u->faction->locale, keyword(K_MOVE)));
sbs_strcat(&sbcmd, " ");
sbs_strcat(&sbcmd, LOC(u->faction->locale, directions[dir]));
moves = 1;
@ -2309,8 +2303,8 @@ int follow_ship(unit * u, order * ord)
rc = rconnect(rc, dir);
while (rc && moves < speed && (dir = hunted_dir(rc->attribs, id)) != NODIRECTION) {
const char *loc = LOC(u->faction->locale, directions[dir]);
bufp = STRLCPY_EX(bufp, " ", &size, "hunt");
bufp = STRLCPY_EX(bufp, loc, &size, "hunt");
sbs_strcat(&sbcmd, " ");
sbs_strcat(&sbcmd, loc);
moves++;
rc = rconnect(rc, dir);
}

View file

@ -495,6 +495,7 @@ static void test_follow_ship_msg(CuTest * tc) {
follow_ship(u, ord);
CuAssertPtrEquals(tc, r, u->region);
CuAssertPtrNotNull(tc, msg = test_find_messagetype(u->faction->msgs, "error18"));
p = msg->parameters[2].v;
CuAssertPtrNotNull(tc, p);

View file

@ -769,11 +769,11 @@ static void rp_battles(struct stream *out, faction * f)
while (bm) {
char buf[256];
RENDER(f, buf, sizeof(buf), ("header_battle", "region", bm->r));
newline(out);
centre(out, buf, true);
newline(out);
rp_messages(out, bm->msgs, f, 0, false);
bm = bm->next;
newline(out);
}
}
}
@ -1237,39 +1237,17 @@ void report_region(struct stream *out, const region * r, faction * f)
if (edges)
newline(out);
for (e = edges; e; e = e->next) {
bool first = true;
message *msg;
bufp = buf;
size = sizeof(buf) - 1;
for (d = 0; d != MAXDIRECTIONS; ++d) {
if (!e->exist[d])
continue;
/* this localization might not work for every language but is fine for de and en */
if (first)
bytes = (int)str_strlcpy(bufp, LOC(f->locale, "nr_borderlist_prefix"), size);
else if (e->lastd == d)
bytes = (int)str_strlcpy(bufp, LOC(f->locale, "nr_borderlist_lastfix"), size);
else
bytes = (int)str_strlcpy(bufp, LOC(f->locale, "nr_borderlist_infix"), size);
if (wrptr(&bufp, &size, bytes) != 0)
WARN_STATIC_BUFFER();
bytes = (int)str_strlcpy(bufp, LOC(f->locale, directions[d]), size);
if (wrptr(&bufp, &size, bytes) != 0)
WARN_STATIC_BUFFER();
first = false;
if (e->exist[d]) {
msg = msg_message(e->transparent ? "nr_border_transparent" : "nr_border_opaque",
"object dir", e->name, d);
nr_render(msg, f->locale, buf, sizeof(buf), f);
msg_release(msg);
paragraph(out, buf, 0, 0, 0);
}
}
/* TODO name is localized? Works for roads anyway... */
/* TODO: creating messages during reporting makes them not show up in CR? */
msg = msg_message("nr_borderlist_postfix", "transparent object",
e->transparent, e->name);
bytes = (int)nr_render(msg, f->locale, bufp, size, f);
msg_release(msg);
if (wrptr(&bufp, &size, bytes) != 0)
WARN_STATIC_BUFFER();
*bufp = 0;
paragraph(out, buf, 0, 0, 0);
}
if (edges) {
while (edges) {
@ -1299,7 +1277,6 @@ static void statistics(struct stream *out, const region * r, const faction * f)
}
}
/* print */
newline(out);
m = msg_message("nr_stat_header", "region", r);
nr_render(m, f->locale, buf, sizeof(buf), f);
msg_release(m);
@ -2305,8 +2282,10 @@ report_plaintext(const char *filename, report_context * ctx,
report_travelthru(out, r, f);
}
if (wants_stats && r->seen.mode >= seen_unit)
if (wants_stats && r->seen.mode >= seen_unit) {
statistics(out, r, f);
newline(out);
}
/* Nachrichten an REGION in der Region */
if (r->seen.mode >= seen_travel) {

View file

@ -275,6 +275,11 @@ void sbs_strcpy(struct sbstring *sbs, const char *str)
sbs->end = sbs->begin + len;
}
size_t sbs_length(const struct sbstring *sbs)
{
return sbs->end - sbs->begin;
}
char *str_unescape(char *str) {
char *read = str, *write = str;
while (*read) {

View file

@ -50,6 +50,7 @@ extern "C" {
void sbs_strcat(struct sbstring *sbs, const char *str);
void sbs_strncat(struct sbstring *sbs, const char *str, size_t size);
void sbs_strcpy(struct sbstring *sbs, const char *str);
size_t sbs_length(const struct sbstring *sbs);
/* benchmark for units:
* JENKINS_HASH: 5.25 misses/hit (with good cache behavior)