Tuesday, 28 August 2018

A look at User Authentication Factors (Part 2/2)

An authentication system is made out of authentication factors. There may be multiple factors, but whether a system is Single-factor Authentication, Two-factor Authentication (2FA) or Three-factor Authentication (3FA), depends on the number of different authentication factor types. For example, a simple Login screen is Single-factor authentication, even though the user has to key in both a login id and a password.

Instagram login screen.
Why? There are two authentication factors. But both of them are the same authentication type - Knowledge. That means there is only one authentication factor type in play. Even if you had to key in five passwords to be allowed entry, that would still be Single-factor Authentication.

Examples of Single-factor Authentication

As previously stated, a typical login screen is Single-factor Authentication. So is any type of system that only uses one authentication factor type.

ActiveSG gantry.
Like the gantries in ActiveSG swimming complexes. You scan your NRIC (a Possession authentication factor type), and it opens up.

Unlocking your mobile phone can be done via thumbprint scan (Inherence), facial recognition (Inherence) or a PIN (Knowledge). That's Single-factor Authentication.

Examples of Two-factor Authentication (2FA)

As mentioned previously, using your SingPass is 2FA. You key in your login id and password (Knowledge), then the systems sends an OTP to your mobile phone (Possession) for you to continue the login process.

Automatic Teller Machine.
Using an Automated Teller Machine (ATM) requires you to have your ATM card (Possession) and your PIN number (Knowledge).

The gantries in Changi Airport (all terminals) are 2FA. First, you scan your passport (Possession) and then your thumbprint (Inherence).

Examples of Three-factor Authentication (3FA)

There are virtually no examples of 3FA on websites. Biometrics are all but impossible right now on browsers. (CAPTCHA doesn't count because while it does - kind of - verify that you're not a bot, it can't verify that you're you.) Therefore, we're limited to only two authentication factor types - Knowledge and Possession.

Hi-tech security.

However, advanced security systems might require an electronic pass, a biometric scan and a passcode. That would qualify as 3FA.

That's all...

I just really wanted to explain 2FA. This might be a little more information than required. Hope this was interesting enough!

Thanks for tuning in! I had a scan-dalously good time.
T___T

Saturday, 25 August 2018

A look at User Authentication Factors (Part 1/2)

In 2016, Singapore introduced 2FA to SingPass authentication. It's been two years, and to my mortification most of the people I've met - techs included, oh my God - don't actually know what the term means beyond having to take an extra step (keying in a One-time Password, otherwise known as OTP) while logging in.

So yes, today we will take a look at what 2FA means in security. It's shorthand for "Two-factor Authentication".

Authentication Factors

During authentication, we make use of authentication factors. This could be just a password, or a thumbprint, or a codephrase. Something for the system to identify you by before allowing entry.

There are generally three types of authentication factors - Knowledge, Possession and Inherence.

Knowledge

This factor type is about what you know. It's something you memorize. In its most common form, it's a password, or a PIN number. If you've watched Mission Impossible: Fallout recently, there's this sequence where Tom Cruise's character, Ethan Hunt, supplies a phrase to a fellow agent.

"I am the storm."


Agent: Fate whispers to the warrior.
Ethan Hunt: There's a storm coming.
Agent: And the warrior whispers back...
Ethan Hunt: I am the storm.


"There's a storm coming." and "I am the storm." are the passphrases and those serve as useful examples of Knowledge authentication factor types.

Possession

Possession isn't about exorcism in this context (heh heh) but it's something you have. Something you keep on your person such as a mobile phone or a security token. Using it, the system can send a one-time password which the user can then use for authentication.

A typical RSA token.

Other examples of a Possession authentication factor type are - ATM card, NRIC card and credit card. Again, things you keep on your person.

Inherence

Don't be intimidated by this term - it basically means what you are. Things that are part of you, that we use in authentication. Like thumbprints, retina scans, facial recognition, voice recognition and so on. Biometrics.

Eye scan.

There's even something that scans the inner lining of your ear. It sounds weird as heck, but we live in strange times. Hey, if it works...

Next

Now that we've covered what the different authentication factor types are, let's take a look at how they make up an authentication system!

Friday, 17 August 2018

Spot The Bug: X marks the spot!

Good evening, and we're back for some Spot The Bug madness.

No bugs are safe!


I've got somber news. Jim "The Anvil" Neidhart, Canadian professional wrestler, just passed away on the 13th of this month and with him, a generous chunk of my childhood. I used to spend weekends glued to the TV gleefully watching the big man slam his opponents to the canvas and execute moves a man his size had no right to be making. (Ever seen a 280 pound standing dropkick? Looks as painful as it sounds.) I'd listen entranced at the mad cackles and the trademark snarls during his interviews. And I would go absolutely apeshit when he hit The Anvil Flattener.

Looted from wwe.com

It was reported that he fell and hit his head at home, then passed away at the age of 63. Guess Jim Deidhart, eh? (OK, ouch, that pun was lame even by my standards...)

Anyway. I'm getting all nostalgic just talking about it. So I was hacking together this little page which would feature a list of my favorite YouTube videos featuring Jim Neidhart. And there'd be a screen that would show the video I clicked on. I copied and pasted some old jQuery that would render a table off a JSON object containing all the data; namely, the title and links of the YouTube videos.

Here's the code. I made the JSON object smaller for brevity.
<!DOCTYPE html>
<html>
    <head>
        <title>The Pink and Black Attack!</title>
        <style>
            body
            {
                background-color: #DD8888;
            }

            #tblMain tr td b
            {
                color: #000000;
            }

            #tblMain tr td b
            {
                color: #FFDDDD;
            }

            button
            {
                background-color: #FFDDDD;
                color: #DD8888;
                font-weight: bold;
            }

            div
            {
                width: 300px;
                float: left;
            }
        </style>

        <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js"></script>

        <script>
        $(document).ready(function (x)
        {
            var pbdata =
            [
                {"desc":"Anvil vs Bad News Brown", "link":"7nhMnYjIFYk"},
                {"desc":"Anvil challenges Bulldog", "link":"W3WYWiqkL3M"},
                {"desc":"Classic Promo", "link":"glFtNFtpEPk"},
                {"desc":"Anvil vs Mr Perfect", "link":"0llHaAsPeeE"},
                {"desc":"Anvil shoot interview", "link":"VAoLzHf1UUY"},
                {"desc":"Anvil promo for Bad News Brown", "link":"yeEF-TAmE6c"},
                {"desc":"Hart Foundation promo", "link":"srSJUctZHf4"},
                {"desc":"Hart Foundation Tag Champs", "link":"GRy-w1qjW2g"},
            ];

            var tr;
            var td;
            var btn;

            pbdata.forEach
            (
                function(x)
                {
                    tr = $("<tr></tr>");

                    td = $("<td></td>");
                    td.html(x.desc);   
                    tr.append(td);

                    btn = $("<button>Go</button>");
                    btn.click(function(x)
                    {
                        $("#ifmPlay").attr("src", "http://www.youtube.com/embed/" + x.link);
                    }
                    );

                    td = $("<td></td>");
                    td.append(btn);

                    tr.append(td);

                    $("#tblMain").append(tr);
                }
            )           
        });

        </script>
    </head>
    <body>
        <div>
            <table id="tblMain">
                <tr>
                    <td><b>Description</b></td>
                    <td><b>Link</b></td>
                </tr>
            </table>
        </div>
        <div>
            <iframe id="ifmPlay" width="420" height="315" src="">
            </iframe>
        </div>   
    </body>
</html>


What went wrong

The table rendered just fine. Pink and black, nice huh? Problem was, no video would play when I clicked the buttons.

Why it went wrong

See this? That's what I did to make the button play the video. That was the problem.
btn.click(function(x)
{
    $("#ifmPlay").attr("src", "http://www.youtube.com/embed/" + x.link);
}


I'm really, really used to making callbacks like this...
function(x)
{

}


...so much so I totally forgot this was already within a callback of its own, also using x as the name of the parameter! So x, in this new scope, was referencing the event of the mouseclick instead of the current element in the pbdata object!

                function(x)
                {
                    tr = $("<tr></tr>");

                    td = $("<td></td>");
                    td.html(x.desc);   
                    tr.append(td);

                    btn = $("<button>Go</button>");
                    btn.click(function(x)
                    {
                        $("#ifmPlay").attr("src", "http://www.youtube.com/embed/" + x.link);
                    }
                    );

                    td = $("<td></td>");
                    td.append(btn);

                    tr.append(td);

                    $("#tblMain").append(tr);
                }


So instead of the URL being "http://www.youtube.com/embed/7nhMnYjIFYk" for example, it would only be "http://www.youtube.com/embed/", which would still get to YouTube, but return absolutely nothing but a blank screen. And there would be no error message in the console because x was still a valid object.

How I fixed it

This one was straightforward. Merely use a different parameter name.
btn.click(function(y)
{
    $("#ifmPlay").attr("src", "http://www.youtube.com/embed/" + x.link);
}


And all's right with the world!

Conclusion

Getting caught mixing up your scopes is a fairly common problem in jQuery, or even just JavaScript. Luckily, this took me all of five minutes to solve. But this can be a right headscratcher if you see no error messages and the problem looks like the videos themselves.

x-quisitely yours,
T___T

Monday, 13 August 2018

Google's Fair Use Defense Overturned

Back in 2016, I heaved a sigh of relief when Google emerged the winner in the ongoing lawsuit pursued by Oracle, in their claim that Google infringed on Java's copyright while building the Android platform. And back then, I resolved to follow the case and see what else Oracle came up with. Fast forward to 2018, and I'm not doing much Android development these days. Heck, I'm not even doing much Java, to be honest. What with work and school combined, and making what little time I had left for the girlfriend, I completely missed the news that Oracle had brought the fight to Google again. And this time, they won. A case of third time lucky?

The case was brought up in the Federal Courts where the previous judgement pertaining to the "Fair Use" defense, was overturned. Apparently, Google was found guilty of not only using Java APIs, but building their own version of the Java Virtual Machine (JVM) that Android apps could run on... and then failing to obtain a license from Oracle for that. Yep, that one piece of documentation turned out to be Google's undoing and now they owe Oracle about 9 billion dollars.

A whole lot of money.
And yep, that's a whole lot of money. Probably chump change to Google though.

The implication here is that any distributor who's built any system on top of that ostensibly free Android platform may now owe Oracle if they choose to pursue. Asus, Xiaomi, Samsung... the list is a lot longer and scarier than I could reproduce off the top of my head. And that cost, though I fervently hope otherwise, may trickle down to the consumer.

But Java is Open-source!

Yes. And yes, the Android platform was distributed free-of-charge to other vendors and Google didn't earn a single penny from Android platform sales directly. But this did help the Android platform grow to their current size, and they are getting a healthy amount of advertising revenue off that. 42 billion, if Oracle are to be believed.

Unfortunately, even if they were earning zilch from this, that argument still doesn't hold water. The issue is that Google violated copyright, not how much they earned from doing so. If I were rich and you went all Robin Hood on me, stealing my money and giving it all away to the poor, that would still be stealing.

Then again, if there was no money to be gained from suing Google, Oracle would hardly bother, would they?

The layman's version

All that jazz about Java APIs and whatnot can be intimidating, I know. Let's look at it in simpler terms.

One sleek, black Nissan.

Suppose you manufacture Nissan. Nice car, great engine. And then someone takes a Nissan apart, and uses most of the parts, along with some other parts, to make a whole new car that kind of looks like your Nissan, but performs in a different way. And then still calls it a Nissan, or a Nissan 2.0. Now people are buying that Nissan instead of yours. That's pretty much what is happening here.

The silver lining in all this...

Java can still be used for free for non-commercial projects. That hasn't changed. Even for independent developers, it's free as long as they don't attempt to build their own platform off Java code and distribute it as a competing platform. And even if they do build their own platform that may not be compatible with the JVM, all that's required is a license.

Although, who wants to develop in Java with that Sword of Damocles hanging over your head? That's an innovation stifler, right there. But at least the line has been drawn, and the message is loud and clear. Do what you want, just don't cross that line.

The fight goes on?

Google's not letting this go. But legal minds more brilliant than mine will be needed to figure a way out of this!

Java good one,
T___T

Tuesday, 7 August 2018

About The Probationary Pay Gambit

One of the many little tricks from small company employers that I've grown to detest, is what I call The Probationary Pay Gambit. I alluded to this in a blogpost in 2015, but today, I'd like to give it even more attention.

Simply put, this is when the employer tells the employee that his pay will be increased after passing the probationary period. It doesn't sound too bad when you say it like that, because, who doesn't want more money, right? But no, this is actually a case of the employer wanting to pay less money.

Let's say, for instance, you ask for 3,000 SGD a month. The employer says, sure, but first we have to put you through a probationary period of, say, three months. During which we will pay you a reduced figure, maybe 2,500 SGD per month. You'll only get paid 3,000 SGD per month after passing the probationary period. (Take note of these example figures, we'll be using them again later on.)

I know. Christ on a stick, right? That's what a lot of small companies do. Many big companies don't even bother with this nonsense. Guess why? Because unlike these small-time players, they don't have to pinch those pennies.

What's that, you say? Employers aren't the cheap bastards I'm painting them as? They actually have valid reasons for The Probationary Pay Gambit? Sure, let's examine some of these reasons.

Why employers think it's fair

"It'll motivate them to do better."

Some employers think a raise after passing probation serves as motivation. These employers are, for lack of a better term, fucking delusional. If an employee is worth 3,000 SGD, paying this employee less isn't going to be much of a motivation to stay. The only reason they would be motivated is if they were worth less than 3,000 SGD a month in the first place.

"Prove that you're worth the money."

Some employers think that this is fair because it protects the company from paying the full amount for a potentially bad hire. If a developer still needed to prove himself to the employer after being hired, there's something abysmally wrong with the employer's hiring practices. Ideally, you want to hire people who have already done shit and aren't interested in having to prove anything.

Besides, this is petty. If the employee turns out to be a bad hire, paying him the full amount from the start would only cost an extra (500 x 3 = 1,500) SGD - not a big deal to a company that isn't dogshit financially already on shaky ground.

Oh no, I'm gonna have to pay!

"It's a reward for doing well."

Get real, please! If an employee performs well during the probationary period, codes his heart out and stays in the office twelve hours a day programming like a boss, the employer isn't going to think, gee, what a bargain! He's going to think, oh crap, I'm used to paying only 2,500 a month for that standard of work, soon I'll have to pay 3,000! That's human nature. Look at Grab who have recently started charging their users realistic cab fares compared to the bargain prices they constantly had years ago. Is there anyone feeling grateful that they got all those cheap cab rides instead of whining like little bitches as to how expensive the rides are now?

How The Probationary Pay Gambit would be fair...

... or a little less one-sided in terms of favoring the employer. Because, let's face it, as it stands, there's no way a deal like that is fair to an employee.

Make up the difference. You want to pay less during probation? Fine. After it's over, not only do you pay the poor sucker the full amount as agreed, you also pay him the money you've been withholding during the probationary period. So if you paid him less 500 SGD per month during a three-month probationary period, you also give him a one-time payment of (500 x 3 = 1,500) SGD after the probationary period ends. Oh, that doesn't sound fair to you? Were you really so looking forward to saving a measly 1,500 SGD?

Discount your expectations accordingly. 2500 SGD is roughly 83% of 3000 SGD. So if you're going to pay the employer only 83% of his salary during the probationary period, it would be fair to expect only 83% effort. Oh, what's that? You expect 100% effort but only want to pay the employee 83% of his wages? You think saying it like that makes you look bad? Well, no shit, Sherlock!

Squeeze 'em, baby. Squeeze.

If the employee is an intern. Interns are expected to prove themselves. That's why they're called interns. They need the experience... and if you are an exploitative asshole who will squeeze them for every drop of blood, that's experience. At least they know what to avoid (i.e, people who try The Probationary Pay Gambit) when they go out to work for real.

What to do when an employer tries to pull this?

Do what I do - end the damn interview. Thank them for their time and inform them that the interview is over. If you really want to join that company, tell them these terms are unacceptable. And be prepared to walk if they disagree.

But, if you're an intern or fresh out of school, hey, suck it up and pay your dues.

Moral of the story

Employers either can pay your employee what they ask for, or they can't. We're all professionals. If you want to be taken seriously at all, don't play these silly games. This sort of deal only attracts the under-qualified and desperate. And honestly, if employers are playing these games, they probably deserve developers who are under-qualified and desperate.

Never settle for le$$,
T___T

Thursday, 2 August 2018

Web Tutorial: Styling a Checkbox, Redux: The Toggle

Welcome back! It's August, and I'd like to kick off the month with a web tutorial dedicated to another version of the web tutorial we went through last month - styling a checkbox.

Sometimes there are different ways to represent a true/false value, and we'll explore one such way today - the toggle. It's ubiquitous on many mobile interfaces today, representing some kind of light switch.

For today's tutorial, we'll recycle much of the code from the last one - because I'm a lazy sod, and because laziness is one of a programmer's primary virtues.

Here we go! Here, we've removed all of the styling, but retained the JavaScript code. Of the HTML, only the "wrappers" have remained along with the labels and checkboxes.
<!DOCTYPE html>
<html>
    <head>
        <title>Checkboxes</title>

        <style>

        </style>

        <script>
            function checkbox(id)
            {
                var cb = document.getElementById(id + "_checkbox");

                if (cb.className == "checkbox on")
                {
                    cb.className = "checkbox off";
                    document.getElementById(id).checked = false;
                }
                else
                {
                    cb.className = "checkbox on";
                    document.getElementById(id).checked = true;
                }
            }
        </script>
    </head>

    <body>
        <div class="checkbox_wrapper" onclick="checkbox('cbPrint')">
            <label for="cbPrint">Print</label>
            <input type="checkbox" name="cbPrint" id="cbPrint" value="print" checked>
        </div>

        <br />

        <div class="checkbox_wrapper" onclick="checkbox('cbEmail')">
            <label for="cbEmail">Email</label>
            <input type="checkbox" name="cbEmail" id="cbEmail" value="email">
        </div>
    </body>
</html>


Now, let's do the decent thing and change the names as appropriate. Not because it's functionally necessary, but because it's just sloppy otherwise. Remember, programmers are lazy, not sloppy. There's a difference.
<!DOCTYPE html>
<html>
    <head>
        <title>Toggle</title>

        <style>

        </style>

        <script>
            function toggle(id)
            {
                var tg = document.getElementById(id + "_toggle");

                if (tg.className=="toggle on")
                {
                    tg.className = "toggle off";
                    document.getElementById(id).checked = false;
                }
                else
                {
                    tg.className = "toggle on";
                    document.getElementById(id).checked = true;
                }
            }
        </script>
    </head>

    <body>
        <div class="toggle_wrapper" onclick="toggle('cbPrint')">
            <label for="cbPrint">Print</label>
            <input type="checkbox" name="cbPrint" id="cbPrint" value="print" checked>
        </div>

        <br />

        <div class="toggle_wrapper" onclick="toggle('cbEmail')">
            <label for="cbEmail">Email</label>
            <input type="checkbox" name="cbEmail" id="cbEmail" value="email">
        </div>
    </body>
</html>


So far, as expected.


Style toggle_wrapper so that the alignment of the checkboxes doesn't get too weird. You may also want to style the labels with a font size. I've put a left margin for better spacing.
        <style>
            .toggle_wrapper:after
            {
                display: block;
                content: "";
                clear: both;
            }

            .toggle_wrapper label
            {
                font-size: 16px;
                margin-left: 1em;
            }
        </style>


Nothing spectacular here, move along.


Now, let's get into it. Put in divs for rendering the toggle with. Each of these divs will be styled with the CSS class toggle, and either on or off. The div beside the checkbox that is checked, obviously, will be styled with on.
        <div class="toggle_wrapper" onclick="toggle('cbPrint')">
            <div class="toggle on" id="cbPrint_toggle">

            </div>
            <label for="cbPrint">Print</label>
            <input type="checkbox" name="cbPrint" id="cbPrint" value="print" checked>
        </div>

        <br />

        <div class="toggle_wrapper" onclick="toggle('cbEmail')">
            <div class="toggle off" id="cbEmail_toggle">

            </div>
            <label for="cbEmail">Email</label>
            <input type="checkbox" name="cbEmail" id="cbEmail" value="email">
        </div>


Now here's the CSS class toggle.

width: 30px and height: 15px - to set a nice rectangular area.
border-radius: 7px - to make the corners round.
border: 1px solid #000000 - to outline this nicely.
float: left - so that the HTML doesn't get any funny ideas about its placement.

            .toggle_wrapper label
            {
                font-size: 16px;
                margin-left: 1em;
            }

            .toggle
            {
                width: 30px;
                height: 15px;
                border-radius: 7px;
                border: 1px solid #000000;
                float: left;
            }


Coming along nicely!


Let's define a different color for on and off... dark grey for on and white for off.
            .toggle
            {
                width: 30px;
                height: 15px;
                border-radius: 7px;
                border: 1px solid #000000;
                float: left;
            }

            .toggle.off
            {
                background-color: #FFFFFF;
            }

            .toggle.on
            {
                background-color: #777777;
            }


Looking good...


Now let's do the knob.

We'll use the before pseudoselector to create a circle which is within the wrapper.

width: 50%, height: 100% - half the width and fully the height of the wrapper.
border-radius: 50% - to make it totally round.
cursor: pointer - because we want users to know you can interact with this thing.
transition: all 0.2s - because that's friggin' cool, that's why.


            .toggle.on
            {
                background-color: #777777;
            }

            .toggle:before
            {
                display: block;
                content: "";
                width: 50%;
                height: 100%;
                border-radius: 50%;
                background-color: #444444;
                cursor: pointer;
                -webkit-transition: all 0.2s;
                transition: all 0.2s;
            }


We're nearly there!


Now style using the before pseudoselectors of off and on. If it's off, the knob should be flush left, hence the margin-left property is at 0%. If it's on, the knob should be flush right.
            .toggle:before
            {
                display: block;
                content: "";
                width: 50%;
                height: 100%;
                border-radius: 50%;
                background-color: #444444;
                cursor: pointer;
                -webkit-transition: all 0.2s;
                transition: all 0.2s;
            }

            .toggle.off:before
            {
                margin-left: 0%;
            }

            .toggle.on:before
            {
                margin-left: 50%;
            }


There, see?


Now let's test your toggles by clicking on the knobs. Uh-oh, they're not moving! That's because the code here should be slightly different from the checkboxes we made before. First, change the JavaScript. Comment out (or remove altogether) the first line of the function. We won't need it anymore. The function will now accept two arguments instead of one, the first one being the object itself that has been clicked.
        <script>
            function toggle(tg, id)
            {
                //var tg = document.getElementById(id + "_toggle");

                if (tg.className == "toggle on")
                {
                    tg.className = "toggle off";
                    document.getElementById(id).checked = false;
                }
                else
                {
                    tg.className = "toggle on";
                    document.getElementById(id).checked = true;
                }
            }
        </script>


Since it's the knob and not the wrapper that's being clicked (and having its CSS class changed), alter your HTML this way.
    <body>
        <div class="toggle_wrapper">
            <div class="toggle on" id="cbPrint_toggle" onclick="toggle('cbPrint')">

            </div>
            <label for="cbPrint">Print</label>
            <input type="checkbox" name="cbPrint" id="cbPrint" value="print" checked>
        </div>

        <br />

        <div class="toggle_wrapper">
            <div class="toggle off" id="cbEmail_toggle" onclick="toggle('cbEmail')">

            </div>
            <label for="cbEmail">Email</label>
            <input type="checkbox" name="cbEmail" id="cbEmail" value="email">
        </div>
    </body>


And then add this to the calls to the toggle() function. Also, remove the ids of the knobs. Won't need them anymore.
    <body>
        <div class="toggle_wrapper">
            <div class="toggle on" onclick="toggle(this, 'cbPrint')">

            </div>
            <label for="cbPrint">Print</label>
            <input type="checkbox" name="cbPrint" id="cbPrint" value="print" checked>
        </div>

        <br />

        <div class="toggle_wrapper">
            <div class="toggle off" onclick="toggle(this, 'cbEmail')">

            </div>
            <label for="cbEmail">Email</label>
            <input type="checkbox" name="cbEmail" id="cbEmail" value="email">
        </div>
    </body>


Ah, now it's working!


One final touch...
            .toggle_wrapper label
            {
                font-size: 16px;
                margin-left: 1em;
            }

            .toggle_wrapper input
            {
                display: none;
            }


Sweet!



But hey, don't take my word for it. Check it out here.




I know, I know...

This isn't the prettiest toggle in the world. You could probably do better. But hey, make it work first. Aesthetics later.

What's that, you say? It looks like some three-year old drew this? Come on now, that's hurtful. All kidding aside, the real toggles used in your mobile interfaces are smaller and more compact, and some even have 3D effects. You may want to experiment with those.

That's about it. Off with you, now.
T___T