dilbert

Fiepke.nl

nixcraft

Slashdot

The Daily WTF

XKCD

Toothpaste for Dinner

Ars Tech

Google News

PBFComics

linux voice

SMBC

coindesk

MAGPIE php feed reader

Representative Line: Aggregation of Concatenation

A few years back, JSON crossed the ?really good hammer? threshold. It has a good balance of being human readable, relatively compact, and simple to parse. It thus has become the go-to format for everything. ?KoHHeKT? inherited a service which generates some JSON from an in-memory tree structure. This is exactly the kind of situation where JSON shines, and it would be trivial to employ one of the many JSON serialization libraries available for C# to generate JSON on demand.

Orrrrr? you could use LINQ aggregations, string formatting and trims?

private static string GetChildrenValue(int childrenCount)
{
        string result = Enumerable.Range(0, childrenCount).Aggregate("", (s, i) => s + $"\"{i}\",");
        return $"[{result.TrimEnd(',')}]";
}

Now, the concatenation and trims and all of that is bad. But I?m mostly stumped by what this method is supposed to accomplish. It?s called GetChildrenValue, but it doesn?t return a value- it returns an array of numbers from 0 to children count. Well, not an array, obviously- a string that can be parsed into an array. And they?re not actually numbers- they?re enclosed in quotes, so it?s actually text, not that any JavaScript client would care about the difference.

Why? How is this consumed? KoHHeKT couldn?t tell us, and we certainly aren?t going to figure it out from this block. But it is representative of the entire JSON constructing library- aggregations and concatenations with minimal exception handling and no way to confirm that it output syntactically valid JSON because nothing sanitizes its inputs.

[Advertisement] Forget logs. Next time you're struggling to replicate error, crash and performance issues in your apps - Think Raygun! Installs in minutes. Learn more.

The New Guy (Part I)

After working mind-numbing warehouse jobs for several years, Jesse was ready for a fresh start in Information Technology. The year 2015 brought him a newly-minted Computer and Networking Systems degree from Totally Legit Technical Institute. It would surely help him find gainful employment, all he had to do was find the right opportunity.

DNS hierarchy Seeking the right opportunity soon turned in to any opportunity. Jesse came across a posting for an IT Systems Administrator that piqued his interest but the requirements and responsibilities left a lot to be desired. They sought someone with C++ and Microsoft Office experience who would perform "General IT Admin Work" and "Other Duties as assigned". None of those things seemed to fit together, but he applied anyway.

During the interview, it became clear that Jesse and this small company were essentially in the same boat. While he was seeking any IT employment, they were seeking any IT Systems admin. Their lone admin recently departed unexpectedly and barely left any documentation of what he actually did. Despite several red flags about the position, he decided to accept anyway. Jesse was assured of little oversight and freedom to do things his way - an extreme rarity for a young IT professional.

Jesse got to work on his first day determined to map out the minefield he was walking in to. The notepad with all the admin passwords his predecessor left behind was useful for logging in to things. Over the next few days, he prodded through the network topology to uncover all the horrors that lie within. Among them:

  • The front-end of their most-used internal application was using Access 97 that interfaced with a SQL Server 2008 machine
  • The desktop computers were all using Windows XP (Half of them upgraded from NT 4.0)
  • The main file server and domain controller were still running on NT 4.0
  • There were two other mystery servers that didn't seem to perform any discernible function. Jesse confirmed this by unplugging them and leaving them off

While sorting through the tangled mess he inherited, Jesse got a high priority email from Ralph, the ancient contracted Networking Admin whom he hadn't yet had the pleasure of meeting. "U need to fix the website. FTP not working." While Ralph wasn't one for details, Jesse did learn something from him - they had a website, it used FTP for something, and it was on him to fix it.

Jesse scanned the magic password notepad and came across something called "Website admin console". He decided to give that a shot, only to be told the password was expired and needed to be reset. Unfortunately the reset email was sent to his predecessor's deactivated account. He replied to Ralph telling him he wasn't able to get to the admin console to fix anything.

All that he got in return was a ticket submitted by a customer explaining the problem and the IP address of the FTP server. It seemed they were expecting to be able to fetch PDF reports from an FTP location and were no longer able to. He went to the FTP server and didn't find anything out of the ordinary, other than the fact that is should really be using SFTP. Despite the lack of security, something was still blocking the client from accessing it.

Jesse suddenly had an idea born of inexperience for how to fix the problem. When he was having connectivity issues on his home WiFi network, all he had to do was reboot the router and it would work! That same logic could surely apply here. After tracking down the router, he found the outlet wasn't easily accessible. So he decided to hit the (factory) Reset button on the back.

Upon returning to his desk, he was greeted by nearly every user in their small office. Nobody's computer worked any more. After turning a deep shade of red, Jesse assured everyone he would fix it. He remembered something from TL Tech Institute called DNS that was supposed to let computers talk to each other. He went around and set everyone's DNS server to 192.168.1.0, the address they always used in school. It didn't help.

Jesse put in a call to Ralph and explained the situation. All he got was a lecture from the gravelly-voiced elder on the other end, "You darn kids! Why don't ye just leave things alone! I've been working networks since before there were networks! Give me a bit, I'll clean up yer dang mess!" Within minutes, Ralph managed to restore connectivity to the office. Jesse checked his DNS settings out of curiosity to find that the proper setting was 2.2.2.0.

The whole router mishap made him completely forget about the original issue - the client's FTP. Before he could start looking at it again, Ralph forwarded him an email from the customer thanking them for getting their reports back. Jesse had no idea how or why that was working now, but he was willing to accept the praise. He solved his first problem, but the fun was just beginning...

To be continued...

[Advertisement] ProGet supports your applications, Docker containers, and third-party packages, allowing you to enforce quality standards across all components. Download and see how!

Error'd: Perfectly Technical Difficulties

David G. wrote, "For once, I'm glad to see technical issues being presented in a technical way."

"Springer has a very interesting pricing algorithm for downloading their books: buy the whole book at some 10% of the sum of all its individual chapters," writes Bernie T.

"While browsing PlataGO! forums, I noticed the developers are erasing technical debt...and then some," Dariusz J. writes.

Bill K. wrote, "Hooray! It's an 'opposite sale' on Adidas' website!"

"A trail camera disguised at a salad bowl? Leave that at an all you can eat buffet and it'll blend right in," wrote Paul T.

Brian writes, "Amazon! That's not how you do math!"

[Advertisement] ProGet supports your applications, Docker containers, and third-party packages, allowing you to enforce quality standards across all components. Download and see how!

Improv for Programmers: Inventing the Toaster

We always like to change things up a little bit here at TDWTF, and thanks to our sponsor Raygun, we've got a chance to bring you a little treat, or at least something a little different.

We're back with a new podcast, but this one isn't a talk show or storytelling format, or even a radio play. Remy rounded up some of the best comedians in Pittsburgh who were also in IT, and bundled them up to do some improv, using articles from our site and real-world IT news as inspiration. It's? it's gonna get weird.

Thanks to Erin Ross, CiarŠn ” Conaire, and Josh Cox for lending their time and voices to this project.

Music: "Happy Happy Game Show" Kevin MacLeod (incompetech.com) Licensed under Creative Commons: By Attribution 3.0 License http://creativecommons.org/licenses/by/3.0/

Raygun gives you a window into the real user-experience for your software. With a few minutes of setup, all the errors, crashes, and performance issues will be identified for you, all in one tool. Not only does it make your applications better, with Raygun APM, it proactively identifies performance issues and builds a workflow for solving them. Raygun APM sorts through the mountains of data for you, surfacing the most important issues so they can be prioritized, triaged and acted on, cutting your Mean Time to Resolution (MTTR) and keeping your users happy.

Now?s the time to sign up. In a few minutes, you can have a build of your app with Raygun integration, and you?ll be surprised at how many issues it can identify. There?s nothing to lose with a 14-day free trial, and there are pricing options available that fit any team size.

[Advertisement] Continuously monitor your servers for configuration changes, and report when there's configuration drift. Get started with Otter today!

CodeSOD: Return of the Mask

Sometimes, you learn something new, and you suddenly start seeing it show up anywhere. The Baader-Meinhof Phenomenon is the name for that. Sometimes, you see one kind of bad code, and the same kind of bad code starts showing up everywhere. Yesterday we saw a nasty attempt to use bitmasks in a loop.

Today, we have Michele?s contribution, of a strange way of interacting with bitmasks. The culprit behind this code was a previous PLC programmer, even if this code wasn?t running straight on the PLC.

public static bool DecodeBitmask(int data, int bitIndex)
{
        var value = data.ToString();
        var padding = value.PadLeft(8, '0');
        return padding[bitIndex] == '1';
}

Take a close look at the parameters there- data is an int. That?s about what you?d expect here? but then we call data.ToString() which is where things start to break down. We pad that string out to 8 characters, and then check and see if a '1' happens to be in the spot we?re checking.

This, of course, defeats the entire purpose and elegance of bit masks, and worse, doesn?t end up being any more readable. Passing a number like 2 isn?t going to return true for any index.

Why does this work this way?

Well, let?s say you wanted a bitmask in the form 0b00000111. You might say, ?well, that?s a 7?. What Michele?s predecssor said was, "that?s text? "00000111". But the point of bitmasks is to use an int to pass data around, so this developer went ahead on and turned "00000111" into an integer by simply parsing it, creating the integer 111. But there?s no possibly way to check if a certain digit is 1 or not, so we have to convert it back into a string to check the bitmask.

Unfortunately, the software is so fragile and unreliable that no one is willing to let the developers make any changes beyond ?it?s on fire, put it out?.

[Advertisement] ProGet supports your applications, Docker containers, and third-party packages, allowing you to enforce quality standards across all components. Download and see how!

CodeSOD: A Bit Masked

The ?for-case? or ?loop-switch? anti-pattern creates some hard to maintain code. You know the drill: the first time through the loop, do one step, the next time through the loop, do a different step. It?s known as the ?Anti-Duff?s Device?, which is a good contrast: Duff?s Device is a clever way to unroll a loop and turn it into a sequential process, while the ?loop-switch? takes a sequential process and turns it into a loop.

Ashlea inherited an MFC application. It was worked on by a number of developers in Germany, some of which used English to name identifiers, some which used German, creating a new language called ?Deunglish?. Or ?Engleutch?? Whatever you call it, Ashlea has helpfully translated all the identifiers into English for us.

Buried deep in a thousand-line ?do everything? method, there?s this block:

if(IS_SOMEFORMATNAME()) //Mantis 24426
{
  if(IS_CONDITION("RELEASE_4"))
  {
    m_BAR.m_TLC_FIELDS.DISTRIBUTIONCHANNEL="";
    CString strKey;
    for (unsigned int i=1; i<16; i++) // Test all combinations
    {
      strKey="#W#X#Y#Z";
      if(i & 1)
        strKey.Replace("#W", m_strActualPromo);  // MANTIS 45587: Search with and without promotion code
      if(i & 2)
        strKey.Replace("#X",m_BAR.m_TLC_FIELDS.OBJECTCODE);
      if(i & 4)
        strKey.Replace("#Y",TOKEN(strFoo,H_BAZCODE));
      if(i & 8)
        strKey.Replace("#Z",TOKEN(strFoo,H_CHAIN));

      strKey.Replace("#W","");
      strKey.Replace("#X","");
      strKey.Replace("#Y","");
      strKey.Replace("#Z","");

      if(m_lDistributionchannel.GetFirst(strKey))
      {
        m_BAR.m_TLC_FIELDS.DISTRIBUTIONCHANNEL="R";
        break;
      }
    }
  }
  else
    m_BAR.m_TLC_FIELDS.DISTRIBUTIONCHANNEL=m_lDistributionchannel.GetFirstLine(m_BAR.m_TLC_FIELDS.OBJECTCODE+m_strActualPromo);
}

Here, we see a rather unique approach to using a for-case- by using bitmasks to combine steps on each iteration of the loop. From what I can tell, they have four things which can combine to make an identifier, but might get combined in many different ways. So they try every possible combination, and if it exists, they can set the DISTRIBUTIONCHANNEL field.

That?s ugly and awful, and certainly a WTF, but honestly, that?s not what leapt out to me. It was this line:

if(IS_CONDITION("RELEASE_4"))

It?s quite clear that, as new versions of the software were released, they needed to control which features were enabled and which weren?t. This is probably related to a database, and thus the database may or may not be upgraded to the same release version as the code. So scattered throughtout the code are checks like this, which enable blocks of code at runtime based on which versions match with these flags.

Debugging that must be a joy.

[Advertisement] Forget logs. Next time you're struggling to replicate error, crash and performance issues in your apps - Think Raygun! Installs in minutes. Learn more.

CodeSOD: CONDITION_FAILURE

Oliver Smith sends this representative line:

bool long_name_that_maybe_distracted_someone()
{
  return (execute() ? CONDITION_SUCCESS : CONDITION_FAILURE);
}

Now, we?ve established my feelings on the if (condition) { return true; } else { return false; } pattern. This is just an iteration on that theme, using a ternary, right?

That?s certainly what it looks like. But Oliver was tracking down an unusual corner-case bug and things just weren?t working correctly. As it turns out, CONDITION_SUCCESS and CONDITION_FAILURE were both defined in the StatusCodes enum.

Screenshot of the intellisense which shows CONDITION_FAILURE defined as 2

Yep- CONDITION_FAILURE is defined as 2. The method returns a bool. Guess what happens when you coerce a non-zero integer into a boolean in C++? It turns into true. This method only ever returns true. Ironically, the calling method would then do its own check against the return value, looking to see if it were CONDITION_SUCCESS or CONDITION_FAILURE.

[Advertisement] Otter - Provision your servers automatically without ever needing to log-in to a command prompt. Get started today!

Error'd: Kind of...but not really

"On occasion, SQL Server Management Studio's estimates can be just a little bit off," writes Warrent B.

Jay D. wrote, "On the surface, yeah, it looks like a good deal, but you know, pesky laws of physics spoil all the fun."

"When opening a new tab in Google Chrome I saw a link near the bottom of the screen that suggested I 'Explore the world's iconic locations in 3D'," writes Josh M., "Unfortunately, Google's API felt differently."

Stuart H. wrote, "I think I might have missed out on this deal, the clock was counting up, no I mean down, I mean negative AHHHH!"

"Something tells me this site's programmer is learning how to spell the hard(est) way," Carl W. writes.

"Why limit yourself with one particular resource of the day when you can substitute any resource you want," wrote Ari S.

[Advertisement] BuildMaster allows you to create a self-service release management platform that allows different teams to manage their applications. Explore how!

CodeSOD: A Quick Replacement

Lucio Crusca was doing a bit of security auditing when he found this pile of code, and it is indeed a pile. It is PHP, which doesn?t automatically make it bad, but it makes use of a feature of PHP so bad that they?ve deprecated it in recent versions: the create_function method.

Before we even dig into this code, the create_function method takes a string, runs eval on it, and returns the name of the newly created anonymous function. Prior to PHP 5.3.0 this was their method of doing lambdas. And while the function is officially deprecated as of PHP 7.2.0? it?s not removed. You can still use it. And I?m sure a lot of code probably still does. Like this block?

        public static function markupToPHP($content) {
                if ($content instanceof phpQueryObject)
                        $content = $content->markupOuter();
                /* <php>...</php> to <?php...? > */
                $content = preg_replace_callback(
                        '@<php>\s*<!--(.*?)-->\s*</php>@s',
                        array('phpQuery', '_markupToPHPCallback'),
                        $content
                );
                /* <node attr='< ?php ? >'> extra space added to save highlighters */
                $regexes = array(
                        '@(<(?!\\?)(?:[^>]|\\?>)+\\w+\\s*=\\s*)(\')([^\']*)(?:&lt;|%3C)\\?(?:php)?(.*?)(?:\\?(?:&gt;|%3E))([^\']*)\'@s',
                        '@(<(?!\\?)(?:[^>]|\\?>)+\\w+\\s*=\\s*)(")([^"]*)(?:&lt;|%3C)\\?(?:php)?(.*?)(?:\\?(?:&gt;|%3E))([^"]*)"@s',
                );
                foreach($regexes as $regex)
                        while (preg_match($regex, $content))
                                $content = preg_replace_callback(
                                        $regex,
                                        create_function('$m',
                                                'return $m[1].$m[2].$m[3]."<?php "
                                                        .str_replace(
                                                                array("%20", "%3E", "%09", "&#10;", "&#9;", "%7B", "%24", "%7D", "%22", "%5B", "%5D"),
                                                                array(" ", ">", "       ", "\n", "      ", "{", "$", "}", \'"\', "[", "]"),
                                                                htmlspecialchars_decode($m[4])
                                                        )
                                                        ." ?>".$m[5].$m[2];'
                                        ),
                                        $content
                                );
                return $content;
        }

From what I can determine from the comments and the code, this is taking some arbitrary content in the form <php>PHP CODE HERE</php> and converting it to <?php PHP CODE HERE ?>. I don?t know what happens after this function is done with it, but I?m already terrified.

The inner-loop fascinates me. while (preg_match($regex, $content)) implies that we need to call the replace function multiple times, but preg_replace_callback by default replaces all instances of the matching regex, so there?s absolutely no reason fo the while loop. Then, of course, the use of create_function, which is itself a WTF, but it?s also worth noting that there?s no need to do this dynamically- you could just as easily have declared a callback function like they did above with _markupToPHPCallback.

Lucio adds:

I was looking for potential security flaws: well, I?m not sure this is actually exploitable, because even black hats have limited patience!

[Advertisement] Utilize BuildMaster to release your software with confidence, at the pace your business demands. Download today!

Exponential Backup

The first day of a new job is always an adjustment. There's a fine line between explaining that you're unused to a procedure and constantly saying "At my old company...". After all, nobody wants to be that guy, right? So you proceed with caution, trying to learn before giving advice.

But some things warrant the extra mile. When Samantha started her tenure at a mid-sized firm, it all started out fine. She got a computer right away, which is a nice plus. She met the team, got settled into a desk, and was given a list of passwords and important URLs to get situated. The usual stuff.

After changing her Windows password, she decided to start by browsing the source code repository. This company used Subversion, so she went and downloaded the whole repo so she could see the structure. It took a while, so she got up and got some coffee; when she got back, it had finished, and she was able to see the total size: 300 GB. That's... weird. Really weird. Weirder still, when she glanced over the commit history, it only dated back a year or so.

What could be taking so much space? Were they storing some huge binaries tucked away someplace that the code depended on? She didn't want to make waves, but this just seemed so... inefficiently huge. Now curious, she opened the repo, browsing the folder structure.

Subversion bases everything on folder structure; there is only really one "branch" in Git's thinking, but you can check out any subfolder without taking the whole repository. Inside of each project directory was a layout that is common to SVN repos: a folder called "branches", a folder called "tags", and a folder called "trunk" (Subversion's primary branch). In the branches directory there were folders called "fix" and "feature", and in each of those there were copies of the source code stored under the names of the branches. Under normal work, she'd start her checkout from one of those branch folders, thus only pulling down the code for her branch, and merge into the "trunk" copy when she was all done.

But there was one folder she didn't anticipate: "backups". Backups? But... this is version control. We can revert to an earlier version any time we want. What are the backups for? I must be misunderstanding. She opened one and was promptly horrified to find a series of zip files, dated monthly, all at revision 1.

Now morbidly curious, Samantha opened one of these zips. The top level folder inside the zip was the name of the project; under that, she found branches, tags, trunk. No way. They can't have-- She clicked in, and there it was, plain as day: another backups folder. And inside? Every backup older than the one she'd clicked. Each backup included, presumably, every backup prior to that, meaning that in the backup for October, the backup from January was included nine times, the backup from February eight times, and so on and so forth. Within two years, a floppy disk worth of code would fill a terabyte drive.

Samantha asked her boss, "What will you do when the repo gets too big to be downloaded onto your hard drive?

His response was quick and entirely serious: "Well, we back it up, then we make a new one."

[Advertisement] ProGet supports your applications, Docker containers, and third-party packages, allowing you to enforce quality standards across all components. Download and see how!

Yes == No

For decades, I worked in an industry where you were never allowed to say no to a user, no matter how ridiculous the request. You had to suck it up and figure out a way to deliver on insane requests, regardless of the technical debt they inflicted.

Canada Stop sign.svg

Users are a funny breed. They say things like I don't care if the input dialog you have works; the last place I worked had a different dialog to do the same thing, and I want that dialog here! With only one user saying stuff like that, it's semi-tolerable. When you have 700+ users and each of them wants a different dialog to do the same thing, and nobody in management will say no, you need to start creating table-driven dialogs (x-y coordinates, width, height, label phrasing, field layout within the dialog, different input formats, fonts, colors and so forth). Multiply that by the number of dialogs in your application and it becomes needlessly pointlessly impossibly difficult.

But it never stops there. Often, one user will request that you move a field from another dialog onto their dialog - just for them. This creates all sorts of havoc with validation logic. Multiply it by hundreds of users and you're basically creating a different application for each of them - each with its own validation logic, all in the same application.

After just a single handful of users demanding changes like this, it can quickly become a nightmare. Worse, once it starts, the next user to whom you say no tells you that you did it for the other guy and so you have to do it for them too! After all, each user is the most important user, right?

It doesn't matter that saying no is the right thing to do. It doesn't matter that it will put a zero-value load on development and debugging time. It doesn't matter that sucking up development time to do it means there are less development hours for bug fixes or actual features.

When management refuses to say no, it can turn your code into a Pandora's-Box-o-WTF?

However, there is hope. There is a way to tell the users no without actually saying no. It's by getting them to say it for you and then withdrawing their urgent, can't-live-without-it, must-have-or-the-world-will-end request.

You may ask how?

The trick is to make them see the actual cost of implementing their teeny tiny little feature.

Yes, we can add that new button to provide all the functionality of Excel in an in-app
calculator, but it will take x months (years) to do it, AND it will push back all of the
other features in the queue. Shall I delay the next release and the other feature requests
so we can build this for you, or would you like to schedule it for a future release?

Naturally you'll have to answer questions like "But it's just a button; why would it take that much effort?"

This is a good thing because it forces them down the rabbit hole into your world where you are the expert. Now you get to explain to them the realities of software development, and the full cost of their little request.

Once they realize the true cost that they'd have to pay, the urgency of the request almost always subsides to nice to have and gets pushed forward so as to not delay the scheduled release.

And because you got them to say it for you, you didn't have to utter the word no.

[Advertisement] Otter - Provision your servers automatically without ever needing to log-in to a command prompt. Get started today!

CodeSOD: CHashMap

There?s a phenomenon I think of as the ?evolution of objects? and it impacts novice programmers. They start by having piles of variables named things like userName0, userName1, accountNum0, accountNum1, etc. This is awkward and cumbersome, and then they discover arrays. string* userNames, int[] accountNums. This is also awkward and cumbersome, and then they discover hash maps, and can do something like Map<string, string>* users. Most programmers go on to discover ?wait, objects do that!?

Not so Brian?s co-worker, Dagny. Dagny wanted to write some C++, but didn?t want to learn that pesky STL or have to master templates. Dagny also considered themselves a ?performance junkie?, so they didn?t want to bloat their codebase with peer-reviewed and optimized code, and instead decided to invent that wheel themselves.

Thus was born CHashMap. Now, Brian didn?t do us the favor of including any of the implementation of CHashMap, claiming he doesn?t want to ?subject the readers to the nightmares that would inevitably arise from viewing this horror directly?. Important note for submitters: we want those nightmares.

Instead, Brian shares with us how the CHashMap is used, and from that we can infer a great deal about how it was implemented. First, let?s simply look at some declarations:

    CHashMap bills;
    CHashMap billcols;
    CHashMap summary;
    CHashMap billinfo;

Note that CHashMap does not take any type parameters. This is because it?s ?type ignorant?, which is like being type agnostic, but with more char*. For example, if you want to get, say, the ?amount_due? field, you might write code like this:

    double amount = 0;
    amount = Atof(bills.Item("amount_due"));

Yes, everything, keys and values, is simply a char*. And, as a bonus, in the interests of code clarity, we can see that Dagny didn?t do anything dangerous, like overload the [] operator. It would certainly be confusing to be able to index the hash map like it were any other collection type.

Now, since everything is stored as a char*, it?s onto you to convert it back to the right type, but since chars are just bytes if you don?t look too closely? you can store anything at that pointer. So, for example, if you wanted to get all of a user?s billing history, you might do something like this?

    CHashMap bills;
    CHashMap billcols;
    CHashMap summary;
    CHashMap billinfo;

    int nbills = dbQuery (query, bills, billcols);
    if (nbills > 0) {
        // scan the bills for amounts and/or issues
        double amount;
        double amountDue = 0;
        int unresolved = 0;

        for (int r=0; r<nbills; r++) {
            if (Strcmp(bills.Item("payment_status",r),BILL_STATUS_REMITTED) != 0) {
                billinfo.Clear();
                amount = Atof(bills.Item("amount_due",r));
                if (amount >= 0) {
                    amountDue += amount;
                    if (Strcmp(bills.Item("status",r),BILL_STATUS_WAITING) == 0) {
                        unresolved += 1;
                        billinfo.AddItem ("duedate", FormatTime("YYYY-MM-DD hh:mm:ss",cvtUTC(bills.Item("due_date",r))));
                        billinfo.AddItem ("biller", bills.Item("account_display_name",r));
                        billinfo.AddItem ("account", bills.Item("account_number",r));
                        billinfo.AddItem ("amount", amount);
                    }
                }
                else {
                    amountDue += 0;
                    unresolved += 1;
                    billinfo.AddItem ("duedate", FormatTime("YYYY-MM-DD hh:mm:ss",cvtUTC(bills.Item("due_date",r))));
                    billinfo.AddItem ("biller", bills.Item("account_display_name",r));
                    billinfo.AddItem ("account", bills.Item("account_number",r));
                    billinfo.AddItem ("amount", "???");
                }
                summary.AddItem ("", &billinfo);
            }
        }
    }

Look at that summary.AddItem ("", &billinfo) line. Yes, that is an empty key. Yes, they?re pointing it at a reference to the billinfo (which also gets Clear()ed a few lines earlier, so I have no idea what?s happening there). And yes, they?re doing this assignment in a loop, but don?t worry! CHashMap allows multiple values per key! That "" key will hold everything.

So, you have multi-value keys which can themselves point to nested CHashMaps, which means you don?t need any complicated JSON or XML classes, you can just use CHashMap as your hammer/foot-gun.

    //code which fetches account details from JSON
    CHashMap accounts;
    CHashMap details;
    CHashMap keys;

    rc = getAccounts (userToken, accounts);
    if (rc == 0) {
        for (int a=1; a<=accounts.Count(); a++) {
            cvt.jsonToKeys (accounts.Item(a), keys);
            rc = getAccountDetails (userToken, keys.Item("accountId"), details);
        }
    }
    // Details of getAccounts
    int getAccounts (const char * user, CHashMap& rows) {
      // <snip>
      AccountClass account;
      for (int a=1; a<=count; a++) {
        // Populate the account class
        // <snip>
        rows.AddItem ("", account.jsonify(t));
      }

With this kind of versatility, is it any surprise that pretty much every function in the application depends on a CHashMap somehow? If that doesn?t prove its utility, I don?t know what will. How could you do anything better? Use classes? Don?t make me laugh!

As a bonus, remember this line above? billinfo.AddItem ("duedate", FormatTime("YYYY-MM-DD hh:mm:ss",cvtUTC(bills.Item("due_date",r))))? Well, Brian has this to add:

it?s worth mentioning that our DB stores dates in the typical format: ?YYYY-MM-DD hh:mm:ss?. cvtUTC is a function that converts a date-time string to a time_t value, and FormatTime converts a time_t to a date-time string.

[Advertisement] ProGet can centralize your organization's software applications and components to provide uniform access to developers and servers. Check it out!

Error'd: Version-itis

"No thanks, I'm holding out for version greater than or equal to 3.6 before upgrading," writes Geoff G.

"Looks like Twilio sent me John Doe's receipt by mistake," wrote Charles L.

"Little do they know that I went back in time and submitted my resume via punch card!" Jim M. writes.

Richard S. wrote, "I went to request a password reset from an old site that is sending me birthday emails, but it looks like the reCAPTCHA is no longer available and the site maintainers have yet to notice."

"It's nice to see that this new Ultra Speed Plus? CD burner lives up to its name, but honestly, I'm a bit scared to try some of these," April K. writes.

"Sometimes, like Samsung's website, you have to accept that it's just ok to fail sometimes," writes Alessandro L.

[Advertisement] ProGet supports your applications, Docker containers, and third-party packages, allowing you to enforce quality standards across all components. Download and see how!

CodeSOD: The Same Date

Oh, dates. Oh, dates in Java. They?re a bit of a dangerous mess, at least prior to Java 8. That?s why Java 8 created its own date-time libraries, and why JodaTime was the gold standard in Java date handling for many years.

But it doesn?t really matter what date handling you do if you?re TRWTF. An Anonymous submitter passed along this method, which is meant to set the start and end date of a search range, based on a number of days:

private void setRange(int days){
        DateFormat df = new SimpleDateFormat("yyyy-MM-dd")
        Date d = new Date();
        Calendar c = Calendar.getInstance()
        c.setTime(d);

        Date start =  c.getTime();

        if(days==-1){
                c.add(Calendar.DAY_OF_MONTH, -1);
                assertThat(c.getTime()).isNotEqualTo(start)
        }
        else if(days==-7){
                c.add(Calendar.DAY_OF_MONTH, -7)
                assertThat(c.getTime()).isNotEqualTo(start)
        }
        else if (days==-30){
                c.add(Calendar.DAY_OF_MONTH, -30)
                assertThat(c.getTime()).isNotEqualTo(start)
        }
        else if (days==-365){
                c.add(Calendar.DAY_OF_MONTH, -365)
                assertThat(c.getTime()).isNotEqualTo(start)
        }

        from = df.format(start).toString()+"T07:00:00.000Z"
        to = df.format(d).toString()+"T07:00:00.000Z"
}

Right off the bat, days only has a handful of valid values- a day, a week, a month(ish) or a year(ish). I?m sure passing it as an int would never cause any confusion. The fact that they don?t quite grasp what variables are for is a nice touch. I?m also quite fond of how they declare a date format at the top, but then also want to append a hard-coded timezone to the format, which again, I?m sure will never cause any confusion or issues. The assertThat calls check that the Calendar.add method does what it?s documented to do, making those both pointless and stupid.

But that?s all small stuff. The real magic is that they never actually use the calendar after adding/subtracting dates. They obviously meant to include d = c.getTime() someplace, but forgot. Then, without actually testing the code (they have so many asserts, why would they need to test?) they checked it in. It wasn?t until QA was checking the prerelease build that anyone noticed, ?Hey, filtering by dates doesn?t work,? and an investigation revealed that from and to always had the same value.

[Advertisement] Forget logs. Next time you're struggling to replicate error, crash and performance issues in your apps - Think Raygun! Installs in minutes. Learn more.

CodeSOD: A Password Generator

Every programming language has a *bias* which informs their solutions. Object-oriented languages are biased towards objects, and all the things which follow on. Clojure is all about function application. Haskell is all about type algebra. Ruby is all about monkey-patching existing objects.

In any language, these things can be taken too far. Java's infamous Spring framework leaps to mind. Perl, being biased towards regular expressions, has earned its reputation as being "write only" thanks to regex abuse.

Gert sent us along some Perl code, and I was expecting to see regexes taken too far. To my shock, there weren't any regexes.

Gert's co-worker needed to generate a random 6-digit PIN for a voicemail system. It didn't need to be cryptographically secure, repeats and zeros are allowed (they exist on a keypad, after all!). The Perl-approach for doing this would normally be something like:


sub randomPIN {
  return sprintf("%06u",int(rand(1000000)));
}

Gert's co-worker had a different plan in mind, though.


sub randomPIN {
my $password;
my @num = (1..9);
my @char = ('@','#','$','%','^','&','*','(',')');
my @alph = ('a'..'z');
my @alph_up = ('A'..'Z');

my $rand_num1 = $num[int rand @num];
my $rand_num2 = $num[int rand @num];
my $rand_num3 = $num[int rand @num];
my $rand_num4 = $num[int rand @num];
my $rand_num5 = $num[int rand @num];
my $rand_num6 = $num[int rand @num];

$password = "$rand_num1"."$rand_num2"."$rand_num3"."$rand_num4"."$rand_num5"."$rand_num6";

return $password;
}

This code starts by creating a set of arrays, @num, @char, etc. The only one that matters is @num, though, since this generates a PIN to be entered on a phone keypad and touchtone signals are numeric and also there is no "(" key on a telephone keypad. Obviously, the developer copied this code from a random password function somewhere, which is its own special kind of awful.

Now, what's fascinating is that they initialize @num with the numbers 1 through 9, and then use the rand function to generate a random number from 0 through 8, so that they can select an item from the array. So they understood how the rand function worked, but couldn't make the leap to eliminate the array with something like rand(9).

For now, replacing this function is simply on Gert's todo list.

[Advertisement] BuildMaster allows you to create a self-service release management platform that allows different teams to manage their applications. Explore how!